1 /*
2  * hunt-time: A time library for D programming language.
3  *
4  * Copyright (C) 2015-2018 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.time.Clock;
13 
14 import hunt.stream.Common;
15 import hunt.Exceptions;
16 import hunt.math.Helper;
17 import hunt.time.Constants;
18 import hunt.time.ZoneId;
19 import hunt.time.ZoneRegion;
20 import hunt.time.Duration;
21 import hunt.time.Instant;
22 import hunt.time.ZoneOffset;
23 import hunt.time.util.Common;
24 import hunt.util.DateTime;
25 
26 import core.time : convert;
27 import std.conv;
28 import std.math;
29 
30 import std.concurrency : initOnce;
31 
32 /**
33  * A clock providing access to the current _instant, date and time using a time-zone.
34  * !(p)
35  * Instances of this class are used to find the current _instant, which can be
36  * interpreted using the stored time-zone to find the current date and time.
37  * As such, a clock can be used instead of {@link System#currentTimeMillis()}
38  * and {@link TimeZone#getDefault()}.
39  * !(p)
40  * Use of a {@code Clock} is optional. All key date-time classes also have a
41  * {@code now()} factory method that uses the system clock _in the default time zone.
42  * The primary purpose of this abstraction is to allow alternate clocks to be
43  * plugged _in as and when required. Applications use an object to obtain the
44  * current time rather than a static method. This can simplify testing.
45  * !(p)
46  * Best practice for applications is to pass a {@code Clock} into any method
47  * that requires the current _instant. A dependency injection framework is one
48  * way to achieve this:
49  * !(pre)
50  *  class MyBean {
51  *    private Clock clock;  // dependency inject
52  *    ...
53  *    void process(LocalDate eventDate) {
54  *      if (eventDate.isBefore(LocalDate.now(clock)) {
55  *        ...
56  *      }
57  *    }
58  *  }
59  * </pre>
60  * This approach allows an alternate clock, such as {@link #fixed(Instant, ZoneId) fixed}
61  * or {@link #_offset(Clock, Duration) _offset} to be used during testing.
62  * !(p)
63  * The {@code system} factory methods provide clocks based on the best available
64  * system clock This may use {@link System#currentTimeMillis()}, or a higher
65  * resolution clock if one is available.
66  *
67  * @implSpec
68  * This abstract class must be implemented with care to ensure other classes operate correctly.
69  * All implementations that can be instantiated must be final, immutable and thread-safe.
70  * !(p)
71  * The principal methods are defined to allow the throwing of an exception.
72  * In normal use, no exceptions will be thrown, however one possible implementation would be to
73  * obtain the time from a central time server across the network. Obviously, _in this case the
74  * lookup could fail, and so the method is permitted to throw an exception.
75  * !(p)
76  * The returned instants from {@code Clock} work on a time-scale that ignores leap seconds,
77  * as described _in {@link Instant}. If the implementation wraps a source that provides leap
78  * second information, then a mechanism should be used to "smooth" the leap second.
79  * The Java Time-Scale mandates the use of UTC-SLS, however clock implementations may choose
80  * how accurate they are with the time-scale so long as they document how they work.
81  * Implementations are therefore not required to actually perform the UTC-SLS slew or to
82  * otherwise be aware of leap seconds.
83  * !(p)
84  * Implementations should implement {@code Serializable} wherever possible and must
85  * document whether or not they do support serialization.
86  *
87  * @implNote
88  * The clock implementation provided here is based on the same underlying clock
89  * as {@link System#currentTimeMillis()}, but may have a precision finer than
90  * milliseconds if available.
91  * However, little to no guarantee is provided about the accuracy of the
92  * underlying clock. Applications requiring a more accurate clock must implement
93  * this abstract class themselves using a different external clock, such as an
94  * NTP server.
95  *
96  * @since 1.8
97  */
98 abstract class Clock {
99 
100     /**
101      * Obtains a clock that returns the current _instant using the best available
102      * system clock, converting to date and time using the UTC time-zone.
103      * !(p)
104      * This clock, rather than {@link #systemDefaultZone()}, should be used when
105      * you need the current _instant without the date or time.
106      * !(p)
107      * This clock is based on the best available system clock.
108      * This may use {@link System#currentTimeMillis()}, or a higher resolution
109      * clock if one is available.
110      * !(p)
111      * Conversion from _instant to date or time uses the {@linkplain ZoneOffset#UTC UTC time-zone}.
112      * !(p)
113      * The returned implementation is immutable, thread-safe and {@code Serializable}.
114      * It is equivalent to {@code system(ZoneOffset.UTC)}.
115      *
116      * @return a clock that uses the best available system clock _in the UTC zone, not null
117      */
118     static Clock systemUTC() {
119         return SystemClock.UTC;
120     }
121 
122     /**
123      * Obtains a clock that returns the current _instant using the best available
124      * system clock, converting to date and time using the default time-zone.
125      * !(p)
126      * This clock is based on the best available system clock.
127      * This may use {@link System#currentTimeMillis()}, or a higher resolution
128      * clock if one is available.
129      * !(p)
130      * Using this method hard codes a dependency to the default time-zone into your application.
131      * It is recommended to avoid this and use a specific time-zone whenever possible.
132      * The {@link #systemUTC() UTC clock} should be used when you need the current _instant
133      * without the date or time.
134      * !(p)
135      * The returned implementation is immutable, thread-safe and {@code Serializable}.
136      * It is equivalent to {@code system(ZoneId.systemDefault())}.
137      *
138      * @return a clock that uses the best available system clock _in the default zone, not null
139      * @see ZoneId#systemDefault()
140      */
141     static Clock systemDefaultZone() {
142         // return new SystemClock(ZoneRegion.systemDefault());
143         return SystemClock.DEFAULT();
144     }
145 
146     /**
147      * Obtains a clock that returns the current _instant using the best available
148      * system clock.
149      * !(p)
150      * This clock is based on the best available system clock.
151      * This may use {@link System#currentTimeMillis()}, or a higher resolution
152      * clock if one is available.
153      * !(p)
154      * Conversion from _instant to date or time uses the specified time-zone.
155      * !(p)
156      * The returned implementation is immutable, thread-safe and {@code Serializable}.
157      *
158      * @param zone  the time-zone to use to convert the _instant to date-time, not null
159      * @return a clock that uses the best available system clock _in the specified zone, not null
160      */
161     static Clock system(ZoneId zone) {
162         assert(zone, "zone");
163         if (zone == ZoneOffset.UTC) {
164             return SystemClock.UTC;
165         }
166         return new SystemClock(zone);
167     }
168 
169     //-------------------------------------------------------------------------
170     /**
171      * Obtains a clock that returns the current _instant ticking _in whole milliseconds
172      * using the best available system clock.
173      * !(p)
174      * This clock will always have the nano-of-second field truncated to milliseconds.
175      * This ensures that the visible time ticks _in whole milliseconds.
176      * The underlying clock is the best available system clock, equivalent to
177      * using {@link #system(ZoneId)}.
178      * !(p)
179      * Implementations may use a caching strategy for performance reasons.
180      * As such, it is possible that the start of the millisecond observed via this
181      * clock will be later than that observed directly via the underlying clock.
182      * !(p)
183      * The returned implementation is immutable, thread-safe and {@code Serializable}.
184      * It is equivalent to {@code tick(system(zone), Duration.ofMillis(1))}.
185      *
186      * @param zone  the time-zone to use to convert the _instant to date-time, not null
187      * @return a clock that ticks _in whole milliseconds using the specified zone, not null
188      * @since 9
189      */
190     static Clock tickMillis(ZoneId zone) {
191         return new TickClock(system(zone), TimeConstant.NANOS_PER_MILLI);
192     }
193 
194     //-------------------------------------------------------------------------
195     /**
196      * Obtains a clock that returns the current _instant ticking _in whole seconds
197      * using the best available system clock.
198      * !(p)
199      * This clock will always have the nano-of-second field set to zero.
200      * This ensures that the visible time ticks _in whole seconds.
201      * The underlying clock is the best available system clock, equivalent to
202      * using {@link #system(ZoneId)}.
203      * !(p)
204      * Implementations may use a caching strategy for performance reasons.
205      * As such, it is possible that the start of the second observed via this
206      * clock will be later than that observed directly via the underlying clock.
207      * !(p)
208      * The returned implementation is immutable, thread-safe and {@code Serializable}.
209      * It is equivalent to {@code tick(system(zone), Duration.ofSeconds(1))}.
210      *
211      * @param zone  the time-zone to use to convert the _instant to date-time, not null
212      * @return a clock that ticks _in whole seconds using the specified zone, not null
213      */
214     static Clock tickSeconds(ZoneId zone) {
215         return new TickClock(system(zone), TimeConstant.NANOS_PER_SECOND);
216     }
217 
218     /**
219      * Obtains a clock that returns the current _instant ticking _in whole minutes
220      * using the best available system clock.
221      * !(p)
222      * This clock will always have the nano-of-second and second-of-minute fields set to zero.
223      * This ensures that the visible time ticks _in whole minutes.
224      * The underlying clock is the best available system clock, equivalent to
225      * using {@link #system(ZoneId)}.
226      * !(p)
227      * Implementations may use a caching strategy for performance reasons.
228      * As such, it is possible that the start of the minute observed via this
229      * clock will be later than that observed directly via the underlying clock.
230      * !(p)
231      * The returned implementation is immutable, thread-safe and {@code Serializable}.
232      * It is equivalent to {@code tick(system(zone), Duration.ofMinutes(1))}.
233      *
234      * @param zone  the time-zone to use to convert the _instant to date-time, not null
235      * @return a clock that ticks _in whole minutes using the specified zone, not null
236      */
237     static Clock tickMinutes(ZoneId zone) {
238         return new TickClock(system(zone), TimeConstant.NANOS_PER_MINUTE);
239     }
240 
241     /**
242      * Obtains a clock that returns instants from the specified clock truncated
243      * to the nearest occurrence of the specified duration.
244      * !(p)
245      * This clock will only tick as per the specified duration. Thus, if the duration
246      * is half a second, the clock will return instants truncated to the half second.
247      * !(p)
248      * The tick duration must be positive. If it has a part smaller than a whole
249      * millisecond, then the whole duration must divide into one second without
250      * leaving a remainder. All normal tick durations will match these criteria,
251      * including any multiple of hours, minutes, seconds and milliseconds, and
252      * sensible nanosecond durations, such as 20ns, 250,000ns and 500,000ns.
253      * !(p)
254      * A duration of zero or one nanosecond would have no truncation effect.
255      * Passing one of these will return the underlying clock.
256      * !(p)
257      * Implementations may use a caching strategy for performance reasons.
258      * As such, it is possible that the start of the requested duration observed
259      * via this clock will be later than that observed directly via the underlying clock.
260      * !(p)
261      * The returned implementation is immutable, thread-safe and {@code Serializable}
262      * providing that the base clock is.
263      *
264      * @param baseClock  the base clock to base the ticking clock on, not null
265      * @param tickDuration  the duration of each visible tick, not negative, not null
266      * @return a clock that ticks _in whole units of the duration, not null
267      * @throws IllegalArgumentException if the duration is negative, or has a
268      *  part smaller than a whole millisecond such that the whole duration is not
269      *  divisible into one second
270      * @throws ArithmeticException if the duration is too large to be represented as nanos
271      */
272     static Clock tick(Clock baseClock, Duration tickDuration) {
273         assert(baseClock, "baseClock");
274         assert(tickDuration, "tickDuration");
275         if (tickDuration.isNegative()) {
276             throw new IllegalArgumentException("Tick duration must not be negative");
277         }
278         long tickNanos = tickDuration.toNanos();
279         if (tickNanos % 1000_000 == 0) {
280             // ok, no fraction of millisecond
281         } else if (1000_000_000 % tickNanos == 0) {
282             // ok, divides into one second without remainder
283         } else {
284             throw new IllegalArgumentException("Invalid tick duration");
285         }
286         if (tickNanos <= 1) {
287             return baseClock;
288         }
289         return new TickClock(baseClock, tickNanos);
290     }
291 
292     //-----------------------------------------------------------------------
293     /**
294      * Obtains a clock that always returns the same _instant.
295      * !(p)
296      * This clock simply returns the specified _instant.
297      * As such, it is not a clock _in the conventional sense.
298      * The main use case for this is _in testing, where the fixed clock ensures
299      * tests are not dependent on the current clock.
300      * !(p)
301      * The returned implementation is immutable, thread-safe and {@code Serializable}.
302      *
303      * @param fixedInstant  the _instant to use as the clock, not null
304      * @param zone  the time-zone to use to convert the _instant to date-time, not null
305      * @return a clock that always returns the same _instant, not null
306      */
307     static Clock fixed(Instant fixedInstant, ZoneId zone) {
308         assert(fixedInstant, "fixedInstant");
309         assert(zone, "zone");
310         return new FixedClock(fixedInstant, zone);
311     }
312 
313     //-------------------------------------------------------------------------
314     /**
315      * Obtains a clock that returns instants from the specified clock with the
316      * specified duration added
317      * !(p)
318      * This clock wraps another clock, returning instants that are later by the
319      * specified duration. If the duration is negative, the instants will be
320      * earlier than the current date and time.
321      * The main use case for this is to simulate running _in the future or _in the past.
322      * !(p)
323      * A duration of zero would have no offsetting effect.
324      * Passing zero will return the underlying clock.
325      * !(p)
326      * The returned implementation is immutable, thread-safe and {@code Serializable}
327      * providing that the base clock is.
328      *
329      * @param baseClock  the base clock to add the duration to, not null
330      * @param offsetDuration  the duration to add, not null
331      * @return a clock based on the base clock with the duration added, not null
332      */
333     static Clock _offset(Clock baseClock, Duration offsetDuration) {
334         assert(baseClock, "baseClock");
335         assert(offsetDuration, "offsetDuration");
336         if (offsetDuration.opEquals(Duration.ZERO)) {
337             return baseClock;
338         }
339         return new OffsetClock(baseClock, offsetDuration);
340     }
341 
342     //-----------------------------------------------------------------------
343     /**
344      * Constructor accessible by subclasses.
345      */
346     protected this() {
347     }
348 
349     //-----------------------------------------------------------------------
350     /**
351      * Gets the time-zone being used to create dates and times.
352      * !(p)
353      * A clock will typically obtain the current _instant and then convert that
354      * to a date or time using a time-zone. This method returns the time-zone used.
355      *
356      * @return the time-zone being used to interpret instants, not null
357      */
358     abstract ZoneId getZone();
359 
360     /**
361      * Returns a copy of this clock with a different time-zone.
362      * !(p)
363      * A clock will typically obtain the current _instant and then convert that
364      * to a date or time using a time-zone. This method returns a clock with
365      * similar properties but using a different time-zone.
366      *
367      * @param zone  the time-zone to change to, not null
368      * @return a clock based on this clock with the specified time-zone, not null
369      */
370     abstract Clock withZone(ZoneId zone);
371 
372     //-------------------------------------------------------------------------
373     /**
374      * Gets the current millisecond _instant of the clock.
375      * !(p)
376      * This returns the millisecond-based _instant, measured from 1970-01-01T00:00Z (UTC).
377      * This is equivalent to the definition of {@link System#currentTimeMillis()}.
378      * !(p)
379      * Most applications should avoid this method and use {@link Instant} to represent
380      * an _instant on the time-line rather than a raw millisecond value.
381      * This method is provided to allow the use of the clock _in high performance use cases
382      * where the creation of an object would be unacceptable.
383      * !(p)
384      * The default implementation currently calls {@link #_instant}.
385      *
386      * @return the current millisecond _instant from this clock, measured from
387      *  the Java epoch of 1970-01-01T00:00Z (UTC), not null
388      * @throws DateTimeException if the _instant cannot be obtained, not thrown by most implementations
389      */
390     long millis() {
391         return instant().toEpochMilli();
392     }
393 
394     //-----------------------------------------------------------------------
395     /**
396      * Gets the current _instant of the clock.
397      * !(p)
398      * This returns an _instant representing the current _instant as defined by the clock.
399      *
400      * @return the current _instant from this clock, not null
401      * @throws DateTimeException if the _instant cannot be obtained, not thrown by most implementations
402      */
403     abstract Instant instant();
404 
405     //-----------------------------------------------------------------------
406     /**
407      * Checks if this clock is equal to another clock.
408      * !(p)
409      * Clocks should override this method to compare equals based on
410      * their state and to meet the contract of {@link Object#equals}.
411      * If not overridden, the behavior is defined by {@link Object#equals}
412      *
413      * @param obj  the object to check, null returns false
414      * @return true if this is equal to the other clock
415      */
416     override
417     bool opEquals(Object obj) {
418         return super.opEquals(obj);
419     }
420 
421     /**
422      * A hash code for this clock.
423      * !(p)
424      * Clocks should override this method based on
425      * their state and to meet the contract of {@link Object#hashCode}.
426      * If not overridden, the behavior is defined by {@link Object#hashCode}
427      *
428      * @return a suitable hash code
429      */
430     override
431      size_t toHash() @trusted nothrow {
432         return super.toHash();
433     }
434 
435     //-----------------------------------------------------------------------
436     /**
437      * Implementation of a clock that always returns the latest time from
438      * {@link System#currentTimeMillis()}.
439      */
440     static final class SystemClock : Clock { // , Serializable 
441 
442         static long OFFSET_SEED() {
443             __gshared long _o;
444             // return initOnce!(_o)(DateTime.currentTimeMillis()/1000);
445             // FIXME: Needing refactor or cleanup -@zhangxueping at 4/15/2019, 1:15:39 PM
446             // 
447             return initOnce!(_o)(DateTime.currentTimeMillis()/1000 - 1024);
448         }
449         
450         static SystemClock UTC() {
451             __gshared SystemClock _UTC;
452             return initOnce!(_UTC)(new SystemClock(ZoneOffset.UTC));
453         }
454         
455         static SystemClock DEFAULT() {
456             __gshared SystemClock _DEFAULT;
457             return initOnce!(_DEFAULT)(new SystemClock(ZoneRegion.systemDefault()));
458         }
459 
460 
461         private  ZoneId zone;
462         // We don't actually need a volatile here.
463         // We don't care if _offset is set or read concurrently by multiple
464         // threads - we just need a value which is 'recent enough' - _in other
465         // words something that has been updated at least once _in the last
466         // 2^32 secs (~136 years). And even if we by chance see an invalid
467         // _offset, the worst that can happen is that we will get a -1 value
468         // from getNanoTimeAdjustment, forcing us to update the _offset
469         // once again.
470         private long _offset;
471 
472         this(ZoneId zone) {
473             this.zone = zone;
474             this._offset = OFFSET_SEED;
475         }
476         override
477         ZoneId getZone() {
478             return zone;
479         }
480         override
481         Clock withZone(ZoneId zone) {
482             if (zone.opEquals(this.zone)) {  // intentional NPE
483                 return this;
484             }
485             return new SystemClock(zone);
486         }
487 
488         override
489         long millis() {
490             // System.currentTimeMillis() and VM.getNanoTimeAdjustment(_offset)
491             // use the same time source - System.currentTimeMillis() simply
492             // limits the resolution to milliseconds.
493             // So we take the faster path and call System.currentTimeMillis()
494             // directly - _in order to avoid the performance penalty of
495             // VM.getNanoTimeAdjustment(_offset) which is less efficient.
496             return DateTime.currentTimeMillis();
497         }
498 
499         override
500         Instant instant() {
501             // Take a local copy of _offset. _offset can be updated concurrently
502             // by other threads (even if we haven't made it volatile) so we will
503             // work with a local copy.
504             // long localOffset = _offset;
505             // long adjustment = VM.getNanoTimeAdjustment(localOffset);
506 
507             // if (adjustment == -1) {
508             //     // -1 is a sentinel value returned by VM.getNanoTimeAdjustment
509             //     // when the _offset it is given is too far off the current UTC
510             //     // time. In principle, this should not happen unless the
511             //     // JVM has run for more than ~136 years (not likely) or
512             //     // someone is fiddling with the system time, or the _offset is
513             //     // by chance at 1ns _in the future (very unlikely).
514             //     // We can easily recover from all these conditions by bringing
515             //     // back the _offset _in range and retry.
516 
517             //     // bring back the _offset _in range. We use -1024 to make
518             //     // it more unlikely to hit the 1ns _in the future condition.
519             //     localOffset = System.currentTimeMillis()/1000 - 1024;
520 
521             //     // retry
522             //     // adjustment = VM.getNanoTimeAdjustment(localOffset);
523 
524             //     if (adjustment == -1) {
525             //         // Should not happen: we just recomputed a new _offset.
526             //         // It should have fixed the issue.
527             //         throw new InternalError("Offset " ~ localOffset.to!string ~ " is not _in range");
528             //     } else {
529             //         // OK - recovery succeeded. Update the _offset for the
530             //         // next call...
531             //         _offset = localOffset;
532             //     }
533             // }
534             long nsecs = DateTime.currentTimeNsecs();
535             long localOffset = convert!("nsecs", "seconds")(nsecs);
536             long adjustment = nsecs - localOffset * 1000_000_000L;
537             // import hunt.logging;
538             // import std.string;
539             // version(HUNT_DEBUG) logDebug("(nsecs : %s , msecs : %s , offset: %s )".format(nsecs,localOffset,adjustment));
540             return Instant.ofEpochSecond(localOffset, adjustment);
541         }
542 
543         override
544         bool opEquals(Object obj) {
545             if (cast(SystemClock)(obj) !is null) {
546                 return zone.opEquals((cast(SystemClock) obj).zone);
547             }
548             return false;
549         }
550 
551         override
552         size_t toHash() @trusted nothrow {
553             return zone.toHash() + 1;
554         }
555         override
556         string toString() {
557             return "SystemClock[" ~ zone.to!string ~ "]";
558         }
559         ///@gxc
560         // private void readObject(ObjectInputStream _is) {
561         //     // ensure that _offset is initialized
562         //     _is.defaultReadObject();
563         //     _offset = OFFSET_SEED;
564         // }
565     }
566 
567     //-----------------------------------------------------------------------
568     /**
569      * Implementation of a clock that always returns the same _instant.
570      * This is typically used for testing.
571      */
572     static final class FixedClock : Clock  { //, Serializable
573         private  Instant _instant;
574         private  ZoneId zone;
575 
576         this(Instant fixedInstant, ZoneId zone) {
577             this._instant = fixedInstant;
578             this.zone = zone;
579         }
580         override
581         ZoneId getZone() {
582             return zone;
583         }
584         override
585         Clock withZone(ZoneId zone) {
586             if (zone.opEquals(this.zone)) {  // intentional NPE
587                 return this;
588             }
589             return new FixedClock(_instant, zone);
590         }
591         override
592         long millis() {
593             return _instant.toEpochMilli();
594         }
595         override
596         Instant instant() {
597             return _instant;
598         }
599         override
600         bool opEquals(Object obj) {
601             if (cast(FixedClock)(obj) !is null) {
602                 FixedClock other = cast(FixedClock) obj;
603                 return _instant.opEquals(other._instant) && zone.opEquals(other.zone);
604             }
605             return false;
606         }
607         override
608         size_t toHash() @trusted nothrow {
609             return _instant.toHash() ^ zone.toHash();
610         }
611         override
612         string toString() {
613             return "FixedClock[" ~ _instant.toString ~ "," ~ zone.toString ~ "]";
614         }
615     }
616 
617     //-----------------------------------------------------------------------
618     /**
619      * Implementation of a clock that adds an _offset to an underlying clock.
620      */
621     static final class OffsetClock : Clock  { // , Serializable
622         private enum long serialVersionUID = 2007484719125426256L;
623         private  Clock baseClock;
624         private  Duration _offset;
625 
626         this(Clock baseClock, Duration _offset) {
627             this.baseClock = baseClock;
628             this._offset = _offset;
629         }
630         override
631         ZoneId getZone() {
632             return baseClock.getZone();
633         }
634         override
635         Clock withZone(ZoneId zone) {
636             if (zone.opEquals(baseClock.getZone())) {  // intentional NPE
637                 return this;
638             }
639             return new OffsetClock(baseClock.withZone(zone), _offset);
640         }
641         override
642         long millis() {
643             return MathHelper.addExact(baseClock.millis(), _offset.toMillis());
644         }
645         override
646         Instant instant() {
647             return baseClock.instant().plus(_offset);
648         }
649         override
650         bool opEquals(Object obj) {
651             if (cast(OffsetClock)(obj) !is null) {
652                 OffsetClock other = cast(OffsetClock) obj;
653                 return baseClock.opEquals(other.baseClock) && _offset.opEquals(other._offset);
654             }
655             return false;
656         }
657         override
658         size_t toHash() @trusted nothrow {
659             return baseClock.toHash() ^ _offset.toHash();
660         }
661         override
662         string toString() {
663             return "OffsetClock[" ~ baseClock.toString ~ "," ~ _offset.toString ~ "]";
664         }
665     }
666 
667     //-----------------------------------------------------------------------
668     /**
669      * Implementation of a clock that adds an _offset to an underlying clock.
670      */
671     static final class TickClock : Clock  { //, Serializable
672         private enum long serialVersionUID = 6504659149906368850L;
673         private  Clock baseClock;
674         private  long tickNanos;
675 
676         this(Clock baseClock, long tickNanos) {
677             this.baseClock = baseClock;
678             this.tickNanos = tickNanos;
679         }
680         override
681         ZoneId getZone() {
682             return baseClock.getZone();
683         }
684         override
685         Clock withZone(ZoneId zone) {
686             if (zone.opEquals(baseClock.getZone())) {  // intentional NPE
687                 return this;
688             }
689             return new TickClock(baseClock.withZone(zone), tickNanos);
690         }
691         override
692         long millis() {
693             long millis = baseClock.millis();
694             return millis - MathHelper.floorMod(millis, (tickNanos / 1000_000L));
695         }
696         override
697         Instant instant() {
698             if ((tickNanos % 1000_000) == 0) {
699                 long millis = baseClock.millis();
700                 return Instant.ofEpochMilli(millis - MathHelper.floorMod(millis , (tickNanos / 1000_000L)));
701             }
702             Instant _instant = baseClock.instant();
703             long nanos = _instant.getNano();
704             long adjust = MathHelper.floorMod(nanos , tickNanos);
705             return _instant.minusNanos(adjust);
706         }
707         override
708         bool opEquals(Object obj) {
709             if (cast(TickClock)(obj) !is null) {
710                 TickClock other = cast(TickClock) obj;
711                 return baseClock.opEquals(other.baseClock) && tickNanos == other.tickNanos;
712             }
713             return false;
714         }
715         override
716         size_t toHash() @trusted nothrow {
717             return baseClock.toHash() ^ (cast(int) (tickNanos ^ (tickNanos >>> 32)));
718         }
719         override
720         string toString() {
721             return "TickClock[" ~ baseClock.toString ~ "," ~ Duration.ofNanos(tickNanos).toString ~ "]";
722         }
723     }
724 
725 }