MonotonicClock.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.api;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
/**
* A Clock implementation that combines monotonic timing with wall-clock time.
* <p>
* This class provides precise time measurements using {@link System#nanoTime()}
* while maintaining wall-clock time information in UTC. The wall-clock time
* is computed from the monotonic duration since system start to ensure consistency
* between time measurements.
* <p>
* This implementation is singleton-based and always uses UTC timezone. The clock
* cannot be adjusted to different timezones to maintain consistent monotonic behavior.
* Users needing local time representation should convert the result of {@link #instant()}
* to their desired timezone:
* <pre>{@code
* Instant now = MonotonicClock.now();
* ZonedDateTime local = now.atZone(ZoneId.systemDefault());
* }</pre>
*
* @see System#nanoTime()
* @see Clock
*/
public class MonotonicClock extends Clock {
private static final MonotonicClock CLOCK = new MonotonicClock();
private final long startNanos;
private final Instant startInstant;
/**
* Private constructor to enforce singleton pattern.
* Initializes the clock with the current system time and nanoTime.
*/
private MonotonicClock() {
this.startNanos = System.nanoTime();
this.startInstant = Clock.systemUTC().instant();
}
/**
* Returns the singleton instance of MonotonicClock.
*
* @return the monotonic clock instance
*/
public static MonotonicClock get() {
return CLOCK;
}
/**
* Returns the current instant from the monotonic clock.
* This is a convenience method equivalent to {@code get().instant()}.
*
* @return the current instant using monotonic timing
*/
public static Instant now() {
return get().instant();
}
/**
* Returns the initialization time of this monotonic clock.
* This is a convenience method equivalent to {@code get().start()}.
*
* @return the instant when this monotonic clock was initialized
* @see #startInstant()
*/
public static Instant start() {
return get().startInstant();
}
/**
* Returns the elapsed time since clock initialization.
* This is a convenience method equivalent to {@code get().elapsedTime()}.
*
* @return the duration since clock initialization
*/
public static Duration elapsed() {
return get().elapsedTime();
}
/**
* Returns a monotonically increasing instant.
* <p>
* The returned instant is calculated by adding the elapsed nanoseconds
* since clock creation to the initial wall clock time. This ensures that
* the time never goes backwards and maintains a consistent relationship
* with the wall clock time.
*
* @return the current instant using monotonic timing
*/
@Override
public Instant instant() {
long elapsedNanos = System.nanoTime() - startNanos;
return startInstant.plusNanos(elapsedNanos);
}
/**
* Returns the wall clock time captured when this monotonic clock was initialized.
* <p>
* This instant serves as the base time from which all subsequent {@link #instant()}
* calls are calculated by adding the elapsed monotonic duration. This ensures
* consistency between the monotonic measurements and wall clock time.
*
* @return the initial wall clock instant when this clock was created
* @see #instant()
*/
public Instant startInstant() {
return startInstant;
}
/**
* Returns the duration elapsed since this clock was initialized.
* <p>
* The returned duration is calculated using {@link System#nanoTime()}
* to ensure monotonic behavior. This duration represents the exact time
* span between clock initialization and the current instant.
*
* @return the duration since clock initialization
* @see #startInstant()
* @see #instant()
*/
public Duration elapsedTime() {
long elapsedNanos = System.nanoTime() - startNanos;
return Duration.ofNanos(elapsedNanos);
}
/**
* Returns the zone ID of this clock, which is always UTC.
*
* @return the UTC zone ID
*/
@Override
public ZoneId getZone() {
return ZoneOffset.UTC;
}
/**
* Returns this clock since timezone adjustments are not supported.
* <p>
* This implementation maintains UTC time to ensure monotonic behavior.
* The provided zone parameter is ignored.
*
* @param zone the target timezone (ignored)
* @return this clock instance
*/
@Override
public Clock withZone(ZoneId zone) {
// Monotonic clock is always UTC-based
return this;
}
}