11/*
2- * Copyright (c) 2012, 2019 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2012, 2021 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
6363
6464import java .io .IOException ;
6565import java .io .ObjectInputStream ;
66+ import java .io .ObjectStreamException ;
67+
6668import static java .time .LocalTime .NANOS_PER_MINUTE ;
6769import static java .time .LocalTime .NANOS_PER_SECOND ;
6870import static java .time .LocalTime .NANOS_PER_MILLI ;
7476/**
7577 * A clock providing access to the current instant, date and time using a time-zone.
7678 * <p>
77- * Instances of this class are used to find the current instant, which can be
78- * interpreted using the stored time-zone to find the current date and time.
79- * As such, a clock can be used instead of {@link System#currentTimeMillis()}
79+ * Instances of this abstract class are used to access a pluggable representation of the
80+ * current instant, which can be interpreted using the stored time-zone to find the
81+ * current date and time.
82+ * For example, {@code Clock} can be used instead of {@link System#currentTimeMillis()}
8083 * and {@link TimeZone#getDefault()}.
8184 * <p>
8285 * Use of a {@code Clock} is optional. All key date-time classes also have a
8588 * plugged in as and when required. Applications use an object to obtain the
8689 * current time rather than a static method. This can simplify testing.
8790 * <p>
91+ * As such, this abstract class does not guarantee the result actually represents the current instant
92+ * on the time-line. Instead, it allows the application to provide a controlled view as to what
93+ * the current instant and time-zone are.
94+ * <p>
8895 * Best practice for applications is to pass a {@code Clock} into any method
89- * that requires the current instant. A dependency injection framework is one
90- * way to achieve this:
96+ * that requires the current instant and time-zone . A dependency injection framework
97+ * is one way to achieve this:
9198 * <pre>
9299 * public class MyBean {
93100 * private Clock clock; // dependency inject
99106 * }
100107 * }
101108 * </pre>
102- * This approach allows an alternate clock, such as {@link #fixed(Instant, ZoneId) fixed}
109+ * This approach allows an alternative clock, such as {@link #fixed(Instant, ZoneId) fixed}
103110 * or {@link #offset(Clock, Duration) offset} to be used during testing.
104111 * <p>
105112 * The {@code system} factory methods provide clocks based on the best available
106- * system clock This may use {@link System#currentTimeMillis()}, or a higher
113+ * system clock. This may use {@link System#currentTimeMillis()}, or a higher
107114 * resolution clock if one is available.
108115 *
109116 * @implSpec
110117 * This abstract class must be implemented with care to ensure other classes operate correctly.
111- * All implementations that can be instantiated must be final, immutable and thread-safe.
118+ * All implementations must be thread-safe - a single instance must be capable of be invoked
119+ * from multiple threads without negative consequences such as race conditions.
112120 * <p>
113121 * The principal methods are defined to allow the throwing of an exception.
114122 * In normal use, no exceptions will be thrown, however one possible implementation would be to
126134 * Implementations should implement {@code Serializable} wherever possible and must
127135 * document whether or not they do support serialization.
128136 *
129- * @implNote
130- * The clock implementation provided here is based on the same underlying clock
131- * as {@link System#currentTimeMillis()}, but may have a precision finer than
132- * milliseconds if available.
133- * However, little to no guarantee is provided about the accuracy of the
134- * underlying clock. Applications requiring a more accurate clock must implement
135- * this abstract class themselves using a different external clock, such as an
136- * NTP server.
137+ * @see InstantSource
137138 *
138139 * @since 1.8
139140 */
140- public abstract class Clock {
141+ public abstract class Clock implements InstantSource {
141142
142143 /**
143144 * Obtains a clock that returns the current instant using the best available
@@ -354,7 +355,7 @@ public static Clock fixed(Instant fixedInstant, ZoneId zone) {
354355 //-------------------------------------------------------------------------
355356 /**
356357 * Obtains a clock that returns instants from the specified clock with the
357- * specified duration added
358+ * specified duration added.
358359 * <p>
359360 * This clock wraps another clock, returning instants that are later by the
360361 * specified duration. If the duration is negative, the instants will be
@@ -408,6 +409,7 @@ protected Clock() {
408409 * @param zone the time-zone to change to, not null
409410 * @return a clock based on this clock with the specified time-zone, not null
410411 */
412+ @ Override
411413 public abstract Clock withZone (ZoneId zone );
412414
413415 //-------------------------------------------------------------------------
@@ -428,6 +430,7 @@ protected Clock() {
428430 * the Java epoch of 1970-01-01T00:00Z (UTC), not null
429431 * @throws DateTimeException if the instant cannot be obtained, not thrown by most implementations
430432 */
433+ @ Override
431434 public long millis () {
432435 return instant ().toEpochMilli ();
433436 }
@@ -441,6 +444,7 @@ public long millis() {
441444 * @return the current instant from this clock, not null
442445 * @throws DateTimeException if the instant cannot be obtained, not thrown by most implementations
443446 */
447+ @ Override
444448 public abstract Instant instant ();
445449
446450 //-----------------------------------------------------------------------
@@ -473,32 +477,120 @@ public int hashCode() {
473477 return super .hashCode ();
474478 }
475479
480+ //-----------------------------------------------------------------------
481+ // initial offset
482+ private static final long OFFSET_SEED = System .currentTimeMillis () / 1000 - 1024 ;
483+ // We don't actually need a volatile here.
484+ // We don't care if offset is set or read concurrently by multiple
485+ // threads - we just need a value which is 'recent enough' - in other
486+ // words something that has been updated at least once in the last
487+ // 2^32 secs (~136 years). And even if we by chance see an invalid
488+ // offset, the worst that can happen is that we will get a -1 value
489+ // from getNanoTimeAdjustment, forcing us to update the offset
490+ // once again.
491+ private static long offset = OFFSET_SEED ;
492+
493+ static Instant currentInstant () {
494+ // Take a local copy of offset. offset can be updated concurrently
495+ // by other threads (even if we haven't made it volatile) so we will
496+ // work with a local copy.
497+ long localOffset = offset ;
498+ long adjustment = VM .getNanoTimeAdjustment (localOffset );
499+
500+ if (adjustment == -1 ) {
501+ // -1 is a sentinel value returned by VM.getNanoTimeAdjustment
502+ // when the offset it is given is too far off the current UTC
503+ // time. In principle, this should not happen unless the
504+ // JVM has run for more than ~136 years (not likely) or
505+ // someone is fiddling with the system time, or the offset is
506+ // by chance at 1ns in the future (very unlikely).
507+ // We can easily recover from all these conditions by bringing
508+ // back the offset in range and retry.
509+
510+ // bring back the offset in range. We use -1024 to make
511+ // it more unlikely to hit the 1ns in the future condition.
512+ localOffset = System .currentTimeMillis () / 1000 - 1024 ;
513+
514+ // retry
515+ adjustment = VM .getNanoTimeAdjustment (localOffset );
516+
517+ if (adjustment == -1 ) {
518+ // Should not happen: we just recomputed a new offset.
519+ // It should have fixed the issue.
520+ throw new InternalError ("Offset " + localOffset + " is not in range" );
521+ } else {
522+ // OK - recovery succeeded. Update the offset for the
523+ // next call...
524+ offset = localOffset ;
525+ }
526+ }
527+ return Instant .ofEpochSecond (localOffset , adjustment );
528+ }
529+
530+ //-----------------------------------------------------------------------
531+ /**
532+ * An instant source that always returns the latest time from
533+ * {@link System#currentTimeMillis()} or equivalent.
534+ */
535+ static final class SystemInstantSource implements InstantSource , Serializable {
536+ @ java .io .Serial
537+ private static final long serialVersionUID = 3232399674412L ;
538+ // this is a singleton, but the class is coded such that it is not a
539+ // problem if someone hacks around and creates another instance
540+ static final SystemInstantSource INSTANCE = new SystemInstantSource ();
541+
542+ SystemInstantSource () {
543+ }
544+ @ Override
545+ public Clock withZone (ZoneId zone ) {
546+ return Clock .system (zone );
547+ }
548+ @ Override
549+ public long millis () {
550+ // System.currentTimeMillis() and VM.getNanoTimeAdjustment(offset)
551+ // use the same time source - System.currentTimeMillis() simply
552+ // limits the resolution to milliseconds.
553+ // So we take the faster path and call System.currentTimeMillis()
554+ // directly - in order to avoid the performance penalty of
555+ // VM.getNanoTimeAdjustment(offset) which is less efficient.
556+ return System .currentTimeMillis ();
557+ }
558+ @ Override
559+ public Instant instant () {
560+ return currentInstant ();
561+ }
562+ @ Override
563+ public boolean equals (Object obj ) {
564+ return obj instanceof SystemInstantSource ;
565+ }
566+ @ Override
567+ public int hashCode () {
568+ return SystemInstantSource .class .hashCode ();
569+ }
570+ @ Override
571+ public String toString () {
572+ return "SystemInstantSource" ;
573+ }
574+ @ java .io .Serial
575+ private Object readResolve () throws ObjectStreamException {
576+ return SystemInstantSource .INSTANCE ;
577+ }
578+ }
579+
476580 //-----------------------------------------------------------------------
477581 /**
478582 * Implementation of a clock that always returns the latest time from
479- * {@link System#currentTimeMillis() }.
583+ * {@code SystemInstantSource.INSTANCE }.
480584 */
481585 static final class SystemClock extends Clock implements Serializable {
482586 @ java .io .Serial
483587 private static final long serialVersionUID = 6740630888130243051L ;
484- private static final long OFFSET_SEED =
485- System .currentTimeMillis ()/1000 - 1024 ; // initial offest
486588 static final SystemClock UTC = new SystemClock (ZoneOffset .UTC );
487589
488590 private final ZoneId zone ;
489- // We don't actually need a volatile here.
490- // We don't care if offset is set or read concurrently by multiple
491- // threads - we just need a value which is 'recent enough' - in other
492- // words something that has been updated at least once in the last
493- // 2^32 secs (~136 years). And even if we by chance see an invalid
494- // offset, the worst that can happen is that we will get a -1 value
495- // from getNanoTimeAdjustment, forcing us to update the offset
496- // once again.
497- private transient long offset ;
498591
499592 SystemClock (ZoneId zone ) {
500593 this .zone = zone ;
501- this .offset = OFFSET_SEED ;
502594 }
503595 @ Override
504596 public ZoneId getZone () {
@@ -513,50 +605,13 @@ public Clock withZone(ZoneId zone) {
513605 }
514606 @ Override
515607 public long millis () {
516- // System.currentTimeMillis() and VM.getNanoTimeAdjustment(offset)
517- // use the same time source - System.currentTimeMillis() simply
518- // limits the resolution to milliseconds.
519- // So we take the faster path and call System.currentTimeMillis()
520- // directly - in order to avoid the performance penalty of
521- // VM.getNanoTimeAdjustment(offset) which is less efficient.
608+ // inline of SystemInstantSource.INSTANCE.millis()
522609 return System .currentTimeMillis ();
523610 }
524611 @ Override
525612 public Instant instant () {
526- // Take a local copy of offset. offset can be updated concurrently
527- // by other threads (even if we haven't made it volatile) so we will
528- // work with a local copy.
529- long localOffset = offset ;
530- long adjustment = VM .getNanoTimeAdjustment (localOffset );
531-
532- if (adjustment == -1 ) {
533- // -1 is a sentinel value returned by VM.getNanoTimeAdjustment
534- // when the offset it is given is too far off the current UTC
535- // time. In principle, this should not happen unless the
536- // JVM has run for more than ~136 years (not likely) or
537- // someone is fiddling with the system time, or the offset is
538- // by chance at 1ns in the future (very unlikely).
539- // We can easily recover from all these conditions by bringing
540- // back the offset in range and retry.
541-
542- // bring back the offset in range. We use -1024 to make
543- // it more unlikely to hit the 1ns in the future condition.
544- localOffset = System .currentTimeMillis ()/1000 - 1024 ;
545-
546- // retry
547- adjustment = VM .getNanoTimeAdjustment (localOffset );
548-
549- if (adjustment == -1 ) {
550- // Should not happen: we just recomputed a new offset.
551- // It should have fixed the issue.
552- throw new InternalError ("Offset " + localOffset + " is not in range" );
553- } else {
554- // OK - recovery succeeded. Update the offset for the
555- // next call...
556- offset = localOffset ;
557- }
558- }
559- return Instant .ofEpochSecond (localOffset , adjustment );
613+ // inline of SystemInstantSource.INSTANCE.instant()
614+ return currentInstant ();
560615 }
561616 @ Override
562617 public boolean equals (Object obj ) {
@@ -573,13 +628,6 @@ public int hashCode() {
573628 public String toString () {
574629 return "SystemClock[" + zone + "]" ;
575630 }
576- @ java .io .Serial
577- private void readObject (ObjectInputStream is )
578- throws IOException , ClassNotFoundException {
579- // ensure that offset is initialized
580- is .defaultReadObject ();
581- offset = OFFSET_SEED ;
582- }
583631 }
584632
585633 //-----------------------------------------------------------------------
@@ -684,7 +732,7 @@ public String toString() {
684732
685733 //-----------------------------------------------------------------------
686734 /**
687- * Implementation of a clock that adds an offset to an underlying clock.
735+ * Implementation of a clock that reduces the tick frequency of an underlying clock.
688736 */
689737 static final class TickClock extends Clock implements Serializable {
690738 @ java .io .Serial
@@ -740,4 +788,54 @@ public String toString() {
740788 }
741789 }
742790
791+ //-----------------------------------------------------------------------
792+ /**
793+ * Implementation of a clock based on an {@code InstantSource}.
794+ */
795+ static final class SourceClock extends Clock implements Serializable {
796+ @ java .io .Serial
797+ private static final long serialVersionUID = 235386528762398L ;
798+ @ SuppressWarnings ("serial" ) // Not statically typed as Serializable
799+ private final InstantSource baseSource ;
800+ private final ZoneId zone ;
801+
802+ SourceClock (InstantSource baseSource , ZoneId zone ) {
803+ this .baseSource = baseSource ;
804+ this .zone = zone ;
805+ }
806+ @ Override
807+ public ZoneId getZone () {
808+ return zone ;
809+ }
810+ @ Override
811+ public Clock withZone (ZoneId zone ) {
812+ if (zone .equals (this .zone )) { // intentional NPE
813+ return this ;
814+ }
815+ return new SourceClock (baseSource , zone );
816+ }
817+ @ Override
818+ public long millis () {
819+ return baseSource .millis ();
820+ }
821+ @ Override
822+ public Instant instant () {
823+ return baseSource .instant ();
824+ }
825+ @ Override
826+ public boolean equals (Object obj ) {
827+ return (obj instanceof SourceClock other )
828+ && zone .equals (other .zone )
829+ && baseSource .equals (other .baseSource );
830+ }
831+ @ Override
832+ public int hashCode () {
833+ return baseSource .hashCode () ^ zone .hashCode ();
834+ }
835+ @ Override
836+ public String toString () {
837+ return "SourceClock[" + baseSource + "," + zone + "]" ;
838+ }
839+ }
840+
743841}
0 commit comments