UNPKG

@js-joda/core

Version:

a date and time library for javascript

330 lines (294 loc) 10.2 kB
/** * @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}]`; } }