net.time4j.SystemClock Maven / Gradle / Ivy
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2015 Meno Hochschild,
* -----------------------------------------------------------------------
* This file (SystemClock.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see .
* -----------------------------------------------------------------------
*/
package net.time4j;
import net.time4j.base.ResourceLoader;
import net.time4j.base.TimeSource;
import net.time4j.scale.LeapSeconds;
import net.time4j.scale.TickProvider;
import net.time4j.scale.TimeScale;
import net.time4j.tz.TZID;
import net.time4j.tz.Timezone;
import java.time.Clock;
import java.time.Instant;
/**
* Represents a clock which is based on the clock of the underlying operating system.
*
* @author Meno Hochschild
* @doctags.concurrency {immutable}
*/
/*[deutsch]
* Repräsentiert eine Uhr, die auf dem Taktgeber des Betriebssystems basiert.
*
* @author Meno Hochschild
* @doctags.concurrency {immutable}
*/
public final class SystemClock
implements TimeSource {
//~ Statische Felder/Initialisierungen --------------------------------
private static final int MIO = 1000000;
private static final int MRD = MIO * 1000;
private static final TickProvider PROVIDER;
private static final boolean MONOTON_MODE;
static {
String platform = System.getProperty("java.vm.name");
TickProvider candidate = null;
for (TickProvider temp : ResourceLoader.getInstance().services(TickProvider.class)) {
if (platform.equals(temp.getPlatform())) {
candidate = temp;
break;
}
}
if (candidate == null) {
candidate = new StdTickProvider();
}
PROVIDER = candidate;
MONOTON_MODE = Boolean.getBoolean("net.time4j.systemclock.nanoTime");
}
/**
* Standard implementation.
*
* The system property "net.time4j.systemclock.nanoTime" controls if this clock is internally
* based on the expression {@link System#nanoTime()} (if property is set to "true") or
* {@link System#currentTimeMillis()} (default). The standard case is a clock which is affected by
* OS-triggered time jumps and user adjustments so there is no guarantee for a monotonic time.
*/
/*[deutsch]
* Standard-Implementierung.
*
* Mit der System-Property "net.time4j.systemclock.nanoTime" kann gesteuert werden, ob diese
* Uhr intern auf dem Ausdruck {@link System#nanoTime()} (wenn Property auf "true" gesetzt)
* oder {@link System#currentTimeMillis()} (Standard) basiert. Der Standardfall ist eine Uhr, die
* für Zeitsprünge und manuelle Verstellungen der Betriebssystem-Uhr empfindlich ist, so
* daß keine Garantie für eine monoton ablaufende Zeit gegeben werden kann.
*/
public static final SystemClock INSTANCE = new SystemClock(false, calibrate());
/**
* Monotonic clock based on the best available clock of the underlying operating system.
*
* A side effect of this implementation can be increased nominal precision up to nanoseconds
* although no guarantee is made to ensure nanosecond accuracy. The accuracy is often limited to
* milliseconds. However, the main focus and motivation is realizing a monotonic behaviour by
* delegating to the use of a monotonic clock of the underlying OS. Equivalent to {@code CLOCK_MONOTONIC}
* on a Linux-server.
*
* @see TickProvider#getNanos()
* @since 3.2/4.1
*/
/*[deutsch]
* Monotone Uhr, die auf der besten verfügbaren Uhr des Betriebssystems basiert.
*
* Ein Seiteneffekt dieser Implementierung kann eine erhöhte nominelle Genauigkeit bis hin
* zu Nanosekunden sein. Allerdings ist die reale Genauigkeit oft auf Millisekunden beschränkt.
* Jedoch liegt der Hauptfokus darauf, ein monotones Verhalten dadurch zu realisieren, daß
* eine monotone Uhr des zugrundeliegenden Betriebssystems verwendet wird. Äquivalent zu
* {@code CLOCK_MONOTONIC} auf einem Linux-Server.
*
* @see TickProvider#getNanos()
* @since 3.2/4.1
*/
public static final SystemClock MONOTONIC = new SystemClock(true, calibrate());
//~ Instanzvariablen --------------------------------------------------
private final boolean monotonic;
private final long offset;
//~ Konstruktoren -----------------------------------------------------
private SystemClock(
boolean monotonic,
long offset
) {
super();
this.monotonic = monotonic;
this.offset = offset;
}
//~ Methoden ----------------------------------------------------------
@Override
public Moment currentTime() {
if (this.monotonic || MONOTON_MODE) {
long nanos = this.utcNanos();
return Moment.of(Math.floorDiv(nanos, MRD), (int) Math.floorMod(nanos, MRD), TimeScale.UTC);
} else {
long millis = System.currentTimeMillis();
int nanos = ((int) Math.floorMod(millis, 1000)) * MIO;
return Moment.of(Math.floorDiv(millis, 1000), nanos, TimeScale.POSIX);
}
}
/**
* Yields the current time in milliseconds elapsed since
* [1970-01-01T00:00:00,000Z].
*
* @return count of milliseconds since UNIX epoch without leap seconds
* @see #currentTimeInMicros()
*/
/*[deutsch]
* Liefert die aktuelle seit [1970-01-01T00:00:00,000Z] verstrichene
* Zeit in Millisekunden.
*
* @return count of milliseconds since UNIX epoch without leap seconds
* @see #currentTimeInMicros()
*/
public long currentTimeInMillis() {
if (this.monotonic || MONOTON_MODE) {
long nanos = this.utcNanos();
long secs = LeapSeconds.getInstance().strip(Math.floorDiv(nanos, MRD));
return Math.multiplyExact(secs, 1000) + Math.floorMod(nanos, MIO);
} else {
return System.currentTimeMillis();
}
}
/**
* Yields the current time in microseconds elapsed since
* [1970-01-01T00:00:00,000000Z].
*
* If this clock is based only on {@link System#currentTimeMillis()}
* then this method will just multiply the millisecond value by factor
* {@code 1000}. On many operating systems the precision is limited to
* milliseconds. This is even true if this clock is based on
* {@link System#nanoTime()} because for purpose of calibration even
* here the method {@code System.currentTimeMillis()} must be accessed
* at least one time.
*
* @return count of microseconds since UNIX epoch without leap seconds
*/
/*[deutsch]
* Liefert die aktuelle seit [1970-01-01T00:00:00,000000Z] verstrichene
* Zeit in Mikrosekunden.
*
* Basiert diese Uhr nur auf {@link System#currentTimeMillis()}, wird
* diese Methode lediglich den Millisekundenwert mit dem Faktor {@code 1000}
* multiplizieren. Auf vielen Betriebssystemen ist die Genauigkeit auch
* nur auf Millisekunden begrenzt. Das gilt selbst dann, wenn diese
* Uhr auf {@link System#nanoTime()} basiert, weil hier wenigstens einmal
* zum Zweck der Kalibrierung auf {@code System.currentTimeMillis()}
* zurückgegriffen werden muß.
*
* @return count of microseconds since UNIX epoch without leap seconds
*/
public long currentTimeInMicros() {
if (this.monotonic || MONOTON_MODE) {
long nanos = this.utcNanos();
long secs = LeapSeconds.getInstance().strip(Math.floorDiv(nanos, MRD));
return Math.multiplyExact(secs, MIO) + Math.floorMod(nanos, 1000);
} else {
return Math.multiplyExact(System.currentTimeMillis(), 1000);
}
}
/**
* Yields the current time in microseconds elapsed since
* UTC epoch [1972-01-01T00:00:00,000000Z].
*
* @return count of microseconds since UTC epoch including leap seconds
* @see #currentTimeInMicros()
* @since 3.2/4.1
*/
/*[deutsch]
* Liefert die aktuelle seit [1972-01-01T00:00:00,000000Z] verstrichene
* UTC-Zeit in Mikrosekunden.
*
* @return count of microseconds since UTC epoch including leap seconds
* @see #currentTimeInMicros()
* @since 3.2/4.1
*/
public long realTimeInMicros() {
if (this.monotonic || MONOTON_MODE) {
return Math.floorDiv(this.utcNanos(), 1000);
} else {
long millis = System.currentTimeMillis();
long utc = LeapSeconds.getInstance().enhance(Math.floorDiv(millis, 1000));
return Math.multiplyExact(utc, MIO) + Math.floorMod(millis, 1000) * 1000;
}
}
/**
* Creates a local clock in platform timezone.
*
* Uses the standard clock {@code SystemClock.INSTANCE} and the platform timezone data.
*
* @return local clock in system timezone using the platform timezone data
* @since 3.3/4.2
* @see net.time4j.tz.Timezone#ofSystem()
* @see #INSTANCE
* @see java.util.TimeZone
*/
/*[deutsch]
* Erzeugt eine lokale Uhr in der Plattform-Zeitzone.
*
* Verwendet die Standarduhr {@code SystemClock.INSTANCE} und die Zeitzonendaten der Plattform.
*
* @return local clock in system timezone using the platform timezone data
* @since 3.3/4.2
* @see net.time4j.tz.Timezone#ofSystem()
* @see #INSTANCE
* @see java.util.TimeZone
*/
public static ZonalClock inPlatformView() {
String tzid = "java.util.TimeZone~" + Timezone.ofSystem().getID().canonical();
return new ZonalClock(INSTANCE, tzid);
}
/**
* Creates a local clock in system timezone.
*
* Uses the standard clock {@code SystemClock.INSTANCE}.
*
* @return cached local clock in system timezone using the best available timezone data
* @see net.time4j.tz.Timezone#ofSystem()
* @see #INSTANCE
*/
/*[deutsch]
* Erzeugt eine lokale Uhr in der System-Zeitzone.
*
* Verwendet die Standarduhr {@code SystemClock.INSTANCE}.
*
* @return cached local clock in system timezone using the best available timezone data
* @see net.time4j.tz.Timezone#ofSystem()
* @see #INSTANCE
*/
public static ZonalClock inLocalView() {
return ZonalClock.ofSystem();
}
/**
* Creates a local clock in given timezone.
*
* In order to achieve a monotonic zonal clock, users can use the expression
* {@code new ZonalClock(SystemClock.MONOTONIC, tzid}.
*
* @param tzid timezone id
* @return local clock in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
* @see #INSTANCE
*/
/*[deutsch]
* Erzeugt eine lokale Uhr in der angegebenen Zeitzone.
*
* Um eine monotone zonale Uhr zu erhalten, können Anwender den Ausdruck
* {@code new ZonalClock(SystemClock.MONOTONIC, tzid} verwenden.
*
* @param tzid timezone id
* @return local clock in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
* @see #INSTANCE
*/
public static ZonalClock inZonalView(TZID tzid) {
return new ZonalClock(SystemClock.INSTANCE, tzid);
}
/**
* Creates a local clock in given timezone.
*
* In order to achieve a monotonic zonal clock, users can use the expression
* {@code new ZonalClock(SystemClock.MONOTONIC, tzid}.
*
* @param tzid timezone id
* @return local clock in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
* @see #INSTANCE
*/
/*[deutsch]
* Erzeugt eine lokale Uhr in der angegebenen Zeitzone.
*
* Um eine monotone zonale Uhr zu erhalten, können Anwender den Ausdruck
* {@code new ZonalClock(SystemClock.MONOTONIC, tzid} verwenden.
*
* @param tzid timezone id
* @return local clock in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
* @see #INSTANCE
*/
public static ZonalClock inZonalView(String tzid) {
return new ZonalClock(SystemClock.INSTANCE, tzid);
}
/**
* Equivalent to {@code SystemClock.INSTANCE.currentTime()}.
*
* @return current time using the standard implementation
* @see #INSTANCE
* @since 2.3
*/
/*[deutsch]
* Äquivalent zu {@code SystemClock.INSTANCE.currentTime()}.
*
* @return current time using the standard implementation
* @see #INSTANCE
* @since 2.3
*/
public static Moment currentMoment() {
return SystemClock.INSTANCE.currentTime();
}
/**
* Recalibrates this instance and yields a new copy.
*
* This method is only relevant if this clock is operated in monotonic mode. It is strongly advised
* not to recalibrate during or near a leap second. Please also note that this method might cause jumps
* in time - even backwards.
*
* @return new and recalibrated copy of this instance
* @see #MONOTONIC
* @since 3.2/4.1
*/
/*[deutsch]
* Eicht diese Instanz und liefert eine neue Kopie.
*
* Diese Methode ist nur relevant, wenn diese Uhr im monotonen Modus läuft. Es wird dringend
* angeraten, nicht während oder nahe einer Schaltsekunde zu eichen. Achtung: Diese Methode kann
* Zeitsprünge verursachen - eventuell sogar rückwärts.
*
* @return new and recalibrated copy of this instance
* @see #MONOTONIC
* @since 3.2/4.1
*/
public SystemClock recalibrated() {
return new SystemClock(this.monotonic, calibrate());
}
/**
* Synchronizes this instance with given time source and yields a new copy.
*
* This method is only relevant if this clock is operated in monotonic mode. It is strongly advised
* not to recalibrate during or near a leap second. Please also note that this method might cause jumps
* in time - even backwards.
*
* @param clock another clock which this instance should be synchronized with
* @return synchronized copy of this instance
* @see #MONOTONIC
* @since 3.2/4.1
*/
/*[deutsch]
* Synchronisiert diese Instanz mit der angegebenen Zeitquelle und liefert eine neue Kopie.
*
* Diese Methode ist nur relevant, wenn diese Uhr im monotonen Modus läuft. Es wird dringend
* angeraten, nicht während oder nahe einer Schaltsekunde zu eichen. Achtung: Diese Methode kann
* Zeitsprünge verursachen - eventuell sogar rückwärts.
*
* @param clock another clock which this instance should be synchronized with
* @return synchronized copy of this instance
* @see #MONOTONIC
* @since 3.2/4.1
*/
public SystemClock synchronizedWith(TimeSource> clock) {
Moment time = Moment.from(clock.currentTime());
long compare = (MONOTON_MODE ? System.nanoTime() : PROVIDER.getNanos());
long utc = time.getElapsedTime(TimeScale.UTC);
long instantNanos = Math.multiplyExact(utc, MRD) + time.getNanosecond(TimeScale.UTC);
long newOffset = Math.subtractExact(instantNanos, compare);
return new SystemClock(this.monotonic, newOffset);
}
private static long calibrate() {
// see https://bugs.openjdk.java.net/browse/JDK-8068730 (affects Java 9 or later)
Instant instant = Clock.systemUTC().instant();
long compare = (MONOTON_MODE ? System.nanoTime() : PROVIDER.getNanos());
// handle Instant like POSIX, see real conversion between Instant and j.u.Date
long utc = LeapSeconds.getInstance().enhance(instant.getEpochSecond());
// offset = [instant] - [counter]
long instantNanos = Math.multiplyExact(utc, MRD) + instant.getNano();
return Math.subtractExact(instantNanos, compare);
}
private long utcNanos() {
long nanos = (MONOTON_MODE ? System.nanoTime() : PROVIDER.getNanos());
return Math.addExact(nanos, this.offset);
}
//~ Innere Klassen ----------------------------------------------------
private static class StdTickProvider
implements TickProvider {
//~ Methoden ------------------------------------------------------
@Override
public String getPlatform() {
return "";
}
@Override
public long getNanos() {
return System.nanoTime();
}
}
}