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.Duration; 23 import java.time.Instant; 24 import java.time.ZoneId; 25 import java.time.ZoneOffset; 26 27 /** 28 * A Clock implementation that combines monotonic timing with wall-clock time. 29 * <p> 30 * This class provides precise time measurements using {@link System#nanoTime()} 31 * while maintaining wall-clock time information in UTC. The wall-clock time 32 * is computed from the monotonic duration since system start to ensure consistency 33 * between time measurements. 34 * <p> 35 * This implementation is singleton-based and always uses UTC timezone. The clock 36 * cannot be adjusted to different timezones to maintain consistent monotonic behavior. 37 * Users needing local time representation should convert the result of {@link #instant()} 38 * to their desired timezone: 39 * <pre>{@code 40 * Instant now = MonotonicClock.now(); 41 * ZonedDateTime local = now.atZone(ZoneId.systemDefault()); 42 * }</pre> 43 * 44 * @see System#nanoTime() 45 * @see Clock 46 */ 47 public class MonotonicClock extends Clock { 48 private static final MonotonicClock CLOCK = new MonotonicClock(); 49 50 private final long startNanos; 51 private final Instant startInstant; 52 53 /** 54 * Private constructor to enforce singleton pattern. 55 * Initializes the clock with the current system time and nanoTime. 56 */ 57 private MonotonicClock() { 58 this.startNanos = System.nanoTime(); 59 this.startInstant = Clock.systemUTC().instant(); 60 } 61 62 /** 63 * Returns the singleton instance of MonotonicClock. 64 * 65 * @return the monotonic clock instance 66 */ 67 public static MonotonicClock get() { 68 return CLOCK; 69 } 70 71 /** 72 * Returns the current instant from the monotonic clock. 73 * This is a convenience method equivalent to {@code get().instant()}. 74 * 75 * @return the current instant using monotonic timing 76 */ 77 public static Instant now() { 78 return get().instant(); 79 } 80 81 /** 82 * Returns the initialization time of this monotonic clock. 83 * This is a convenience method equivalent to {@code get().start()}. 84 * 85 * @return the instant when this monotonic clock was initialized 86 * @see #startInstant() 87 */ 88 public static Instant start() { 89 return get().startInstant(); 90 } 91 92 /** 93 * Returns the elapsed time since clock initialization. 94 * This is a convenience method equivalent to {@code get().elapsedTime()}. 95 * 96 * @return the duration since clock initialization 97 */ 98 public static Duration elapsed() { 99 return get().elapsedTime(); 100 } 101 102 /** 103 * Returns a monotonically increasing instant. 104 * <p> 105 * The returned instant is calculated by adding the elapsed nanoseconds 106 * since clock creation to the initial wall clock time. This ensures that 107 * the time never goes backwards and maintains a consistent relationship 108 * with the wall clock time. 109 * 110 * @return the current instant using monotonic timing 111 */ 112 @Override 113 public Instant instant() { 114 long elapsedNanos = System.nanoTime() - startNanos; 115 return startInstant.plusNanos(elapsedNanos); 116 } 117 118 /** 119 * Returns the wall clock time captured when this monotonic clock was initialized. 120 * <p> 121 * This instant serves as the base time from which all subsequent {@link #instant()} 122 * calls are calculated by adding the elapsed monotonic duration. This ensures 123 * consistency between the monotonic measurements and wall clock time. 124 * 125 * @return the initial wall clock instant when this clock was created 126 * @see #instant() 127 */ 128 public Instant startInstant() { 129 return startInstant; 130 } 131 132 /** 133 * Returns the duration elapsed since this clock was initialized. 134 * <p> 135 * The returned duration is calculated using {@link System#nanoTime()} 136 * to ensure monotonic behavior. This duration represents the exact time 137 * span between clock initialization and the current instant. 138 * 139 * @return the duration since clock initialization 140 * @see #startInstant() 141 * @see #instant() 142 */ 143 public Duration elapsedTime() { 144 long elapsedNanos = System.nanoTime() - startNanos; 145 return Duration.ofNanos(elapsedNanos); 146 } 147 148 /** 149 * Returns the zone ID of this clock, which is always UTC. 150 * 151 * @return the UTC zone ID 152 */ 153 @Override 154 public ZoneId getZone() { 155 return ZoneOffset.UTC; 156 } 157 158 /** 159 * Returns this clock since timezone adjustments are not supported. 160 * <p> 161 * This implementation maintains UTC time to ensure monotonic behavior. 162 * The provided zone parameter is ignored. 163 * 164 * @param zone the target timezone (ignored) 165 * @return this clock instance 166 */ 167 @Override 168 public Clock withZone(ZoneId zone) { 169 // Monotonic clock is always UTC-based 170 return this; 171 } 172 }