1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.maven.api;
20
21 import java.time.Clock;
22 import java.time.Instant;
23 import java.time.ZoneId;
24 import java.time.ZoneOffset;
25
26 /**
27 * A Clock implementation that combines monotonic timing with wall-clock time.
28 * <p>
29 * This class provides precise time measurements using {@link System#nanoTime()}
30 * while maintaining wall-clock time information in UTC. The wall-clock time
31 * is computed from the monotonic duration since system start to ensure consistency
32 * between time measurements.
33 * <p>
34 * This implementation is singleton-based and always uses UTC timezone. The clock
35 * cannot be adjusted to different timezones to maintain consistent monotonic behavior.
36 * Users needing local time representation should convert the result of {@link #instant()}
37 * to their desired timezone:
38 * <pre>{@code
39 * Instant now = MonotonicClock.now();
40 * ZonedDateTime local = now.atZone(ZoneId.systemDefault());
41 * }</pre>
42 *
43 * @see System#nanoTime()
44 * @see Clock
45 */
46 public class MonotonicClock extends Clock {
47 private static final MonotonicClock CLOCK = new MonotonicClock();
48
49 private final long startNanos;
50 private final Instant startInstant;
51
52 /**
53 * Private constructor to enforce singleton pattern.
54 * Initializes the clock with the current system time and nanoTime.
55 */
56 private MonotonicClock() {
57 this.startNanos = System.nanoTime();
58 this.startInstant = Clock.systemUTC().instant();
59 }
60
61 /**
62 * Returns the singleton instance of MonotonicClock.
63 *
64 * @return the monotonic clock instance
65 */
66 public static MonotonicClock get() {
67 return CLOCK;
68 }
69
70 /**
71 * Returns the current instant from the monotonic clock.
72 * This is a convenience method equivalent to {@code get().instant()}.
73 *
74 * @return the current instant using monotonic timing
75 */
76 public static Instant now() {
77 return get().instant();
78 }
79
80 /**
81 * Returns a monotonically increasing instant.
82 * <p>
83 * The returned instant is calculated by adding the elapsed nanoseconds
84 * since clock creation to the initial wall clock time. This ensures that
85 * the time never goes backwards and maintains a consistent relationship
86 * with the wall clock time.
87 *
88 * @return the current instant using monotonic timing
89 */
90 @Override
91 public Instant instant() {
92 long elapsedNanos = System.nanoTime() - startNanos;
93 return startInstant.plusNanos(elapsedNanos);
94 }
95
96 /**
97 * Returns the zone ID of this clock, which is always UTC.
98 *
99 * @return the UTC zone ID
100 */
101 @Override
102 public ZoneId getZone() {
103 return ZoneOffset.UTC;
104 }
105
106 /**
107 * Returns this clock since timezone adjustments are not supported.
108 * <p>
109 * This implementation maintains UTC time to ensure monotonic behavior.
110 * The provided zone parameter is ignored.
111 *
112 * @param zone the target timezone (ignored)
113 * @return this clock instance
114 */
115 @Override
116 public Clock withZone(ZoneId zone) {
117 // Monotonic clock is always UTC-based
118 return this;
119 }
120 }