wangzhibo
2026-05-09 53418655c061ac26f66058253f39200aced1ab1d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/**
 * @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}]`;
    }
}