/**
|
* @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper
|
* @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
|
* @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
|
*/
|
|
import { abstractMethodFail, requireNonNull } from './assert';
|
import { Instant } from './Instant';
|
import { ZoneId } from './ZoneId';
|
import { ZoneOffset } from './ZoneOffset';
|
|
/**
|
* A clock providing access to the current instant, date and time using a time-zone.
|
*
|
* Instances of this class are used to find the current instant, which can be
|
* interpreted using the stored time-zone to find the current date and time.
|
* As such, a clock can be used instead of {@link System#currentTimeMillis}
|
* and {@link TimeZone#getDefault}.
|
*
|
* Use of a {@link Clock} is optional. All key date-time classes also have a
|
* `now()` factory method that uses the system clock in the default time zone.
|
* The primary purpose of this abstraction is to allow alternate clocks to be
|
* plugged in as and when required. Applications use an object to obtain the
|
* current time rather than a static method. This can simplify testing.
|
*
|
* Best practice for applications is to pass a {@link Clock} into any method
|
* that requires the current instant.
|
*
|
* This approach allows an alternate clock, such as {@link fixed}
|
* or {@link offset} to be used during testing.
|
*
|
* The {@link system} factory methods provide clocks based on the best available
|
* system clock This may use {@link System#currentTimeMillis}, or a higher
|
* resolution clock if one is available.
|
*
|
* The javascript Clock implementation differs from the openjdk.
|
*
|
* Javascript only provides the UTC millis of epoch and the ZoneOffset in minutes of the system default time.
|
* Javascript do not provide the system default ZoneId.
|
*
|
* the system default ZoneId is only guessable by the ZoneOffset, like moment-timezone does by returning one ZoneId
|
* with the same ZoneOffset.
|
*
|
* Therefore we are doing a shortcut here, by defining a SystemUTCClock and a SystemDefaultClock, the Clock itself
|
* is returning the ZoneOffset and not the ZoneRules as in the jdk. We should change it, when introducing the iana
|
* timezone database and implementing the timezone domains.
|
*
|
*/
|
|
export class Clock {
|
/**
|
* Obtains a clock that returns the current instant using the
|
* system clock, converting to date and time using the Date.getTime() UTC millis.
|
*
|
* This clock, rather than {@link systemDefaultZone}, should be used when
|
* you need the current instant without the date or time.
|
*
|
* @return {Clock} a clock that uses the system clock in the UTC zone, not null
|
*/
|
static systemUTC() {
|
return new SystemClock(ZoneOffset.UTC);
|
}
|
|
/**
|
* Obtains a clock that returns the current instant using the best available
|
* system clock, converting to date and time using the default time-zone.
|
*
|
* This clock is based on the available system clock using the Date.getTime() UTC millis
|
*
|
* Using this method hard codes a dependency to the default time-zone into your application.
|
*
|
* The UTC clock (see {@link systemUTC}) should be used when you need the current instant
|
* without the date or time.
|
*
|
*
|
* @return {Clock} a clock that uses the system clock in the default zone, not null
|
* @see ZoneId#systemDefault()
|
*/
|
static systemDefaultZone() {
|
return new SystemClock(ZoneId.systemDefault());
|
}
|
|
/**
|
*
|
* @param {ZoneId} zone
|
* @return {Clock} a clock that uses the specified time zone
|
*/
|
static system(zone){
|
return new SystemClock(zone);
|
}
|
|
/**
|
* Obtains a clock that always returns the same instant.
|
*
|
* This clock simply returns the specified instant.
|
* As such, it is not a clock in the conventional sense.
|
* The main use case for this is in testing, where the fixed clock ensures
|
* tests are not dependent on the current clock.
|
*
|
* @param {Instant} fixedInstant the instant to use as the clock, not null
|
* @param {ZoneId} zoneId the zoneOffset to use as zone Offset, not null
|
* @return {Clock} a clock that always returns the same instant, not null
|
*/
|
static fixed(fixedInstant, zoneId) {
|
return new FixedClock(fixedInstant, zoneId);
|
}
|
|
/**
|
* Obtains a clock that returns instants from the specified clock with the
|
* specified duration added
|
* <p>
|
* This clock wraps another clock, returning instants that are later by the
|
* specified duration. If the duration is negative, the instants will be
|
* earlier than the current date and time.
|
* The main use case for this is to simulate running in the future or in the past.
|
* <p>
|
* A duration of zero would have no offsetting effect.
|
* Passing zero will return the underlying clock.
|
* <p>
|
* The returned implementation is immutable, thread-safe and {@code Serializable}
|
* providing that the base clock is.
|
*
|
* @param baseClock the base clock to add the duration to, not null
|
* @param duration the duration to add, not null
|
* @return a clock based on the base clock with the duration added, not null
|
*/
|
static offset(baseClock, duration) {
|
return new OffsetClock(baseClock, duration);
|
}
|
|
/**
|
* Gets the current millisecond instant of the clock.
|
*
|
* This returns the millisecond-based instant, measured from 1970-01-01T00:00Z (UTC).
|
* This is equivalent to the definition of {@link Date#getTime}.
|
*
|
* Most applications should avoid this method and use {@link Instant} to represent
|
* an instant on the time-line rather than a raw millisecond value.
|
* This method is provided to allow the use of the clock in high performance use cases
|
* where the creation of an object would be unacceptable.
|
*
|
* The default implementation currently calls {@link instant}.
|
*
|
* @return the current millisecond instant from this clock, measured from
|
* the Java epoch of 1970-01-01T00:00Z (UTC), not null
|
*/
|
millis(){
|
abstractMethodFail('Clock.millis');
|
}
|
|
/**
|
* Gets the current instant of the clock.
|
*
|
* This returns an instant representing the current instant as defined by the clock.
|
*
|
* @return {Instant} the current instant from this clock, not null
|
*/
|
instant(){
|
abstractMethodFail('Clock.instant');
|
}
|
|
zone(){
|
abstractMethodFail('Clock.zone');
|
}
|
|
/**
|
* Returns a copy of this clock with a different time-zone.
|
* <p>
|
* A clock will typically obtain the current instant and then convert that
|
* to a date or time using a time-zone. This method returns a clock with
|
* similar properties but using a different time-zone.
|
*
|
* @return a clock based on this clock with the specified time-zone, not null
|
*/
|
withZone(){
|
abstractMethodFail('Clock.withZone');
|
}
|
}
|
|
/**
|
* Implementation of a clock that always returns the latest time from
|
* {@link Date#getTime}.
|
*
|
* @private
|
*/
|
class SystemClock extends Clock {
|
/**
|
*
|
* @param {!ZoneId} zone
|
*/
|
constructor(zone){
|
requireNonNull(zone, 'zone');
|
super();
|
this._zone = zone;
|
}
|
|
/**
|
*
|
* @returns {!ZoneId}
|
*/
|
zone() {
|
return this._zone;
|
}
|
|
/**
|
*
|
* @returns {number}
|
*/
|
millis() {
|
return new Date().getTime();
|
}
|
|
/**
|
*
|
* @returns {Instant}
|
*/
|
instant() {
|
return Instant.ofEpochMilli(this.millis());
|
}
|
|
equals(obj) {
|
if (obj instanceof SystemClock) {
|
return this._zone.equals(obj._zone);
|
}
|
return false;
|
}
|
|
withZone(zone) {
|
if (zone.equals(this._zone)) { // intentional NPE
|
return this;
|
}
|
return new SystemClock(zone);
|
}
|
|
/**
|
*
|
* @returns {string}
|
*/
|
toString(){
|
return `SystemClock[${this._zone.toString()}]`;
|
}
|
|
}
|
|
/**
|
* Implementation of a clock that always returns the same instant.
|
* This is typically used for testing.
|
* @private
|
*/
|
class FixedClock extends Clock{
|
constructor(fixedInstant, zoneId) {
|
super();
|
this._instant = fixedInstant;
|
this._zoneId = zoneId;
|
}
|
|
instant() {
|
return this._instant;
|
}
|
|
millis(){
|
return this._instant.toEpochMilli();
|
}
|
|
zone() {
|
return this._zoneId;
|
}
|
|
toString(){
|
return 'FixedClock[]';
|
}
|
|
equals(obj) {
|
if (obj instanceof FixedClock) {
|
return this._instant.equals(obj._instant) && this._zoneId.equals(obj._zoneId);
|
}
|
return false;
|
}
|
|
withZone(zone) {
|
if (zone.equals(this._zoneId)) { // intentional NPE
|
return this;
|
}
|
return new FixedClock(this._instant, zone);
|
}
|
|
}
|
|
|
/**
|
* Implementation of a clock that adds an offset to an underlying clock.
|
*/
|
class OffsetClock extends Clock {
|
constructor(baseClock, offset) {
|
super();
|
this._baseClock = baseClock;
|
this._offset = offset;
|
}
|
|
zone() {
|
return this._baseClock.zone();
|
}
|
|
withZone(zone) {
|
if (zone.equals(this._baseClock.zone())) { // intentional NPE
|
return this;
|
}
|
return new OffsetClock(this._baseClock.withZone(zone), this._offset);
|
}
|
|
millis() {
|
return this._baseClock.millis() + this._offset.toMillis();
|
}
|
|
instant() {
|
return this._baseClock.instant().plus(this._offset);
|
}
|
|
equals(obj) {
|
if (obj instanceof OffsetClock) {
|
return this._baseClock.equals(obj._baseClock) && this._offset.equals(obj._offset);
|
}
|
return false;
|
}
|
|
toString() {
|
return `OffsetClock[${this._baseClock},${this._offset}]`;
|
}
|
}
|