tl;dr
Is there a way, either in code or with JVM arguments, to override the current time, as presented via System.currentTimeMillis, other than manually changing the system clock on the host machine?
Yes.
Instant.now(
Clock.fixed(
Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
)
)
Clock
In java.time
We have a new solution to the problem of a pluggable clock replacement to facilitate testing with faux date-time values. The java.time package in Java 8 includes an abstract class java.time.Clock
, with an explicit purpose:
to allow alternate clocks to be plugged in as and when required
You could plug in your own implementation of Clock
, though you likely can find one already made to meet your needs. For your convenience, java.time includes static methods to yield special implementations. These alternate implementations can be valuable during testing.
Altered cadence
The various tick…
methods produce clocks that increment the current moment with a different cadence.
The default Clock
reports a time updated as frequently as milliseconds in Java 8 and in Java 9 as fine as nanoseconds (depending on your hardware). You can ask for the true current moment to be reported with a different granularity.
False clocks
Some clocks can lie, producing a result different than that of the host OS’ hardware clock.
fixed
- Reports a single unchanging (non-incrementing) moment as the current moment.
offset
- Reports the current moment but shifted by the passed Duration
argument.
For example, lock in the first moment of the earliest Christmas this year. in other words, when Santa and his reindeer make their first stop. The earliest time zone nowadays seems to be Pacific/Kiritimati
at +14:00
.
LocalDate ld = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate xmasThisYear = MonthDay.of( Month.DECEMBER , 25 ).atYear( ld.getYear() );
ZoneId earliestXmasZone = ZoneId.of( "Pacific/Kiritimati" ) ;
ZonedDateTime zdtEarliestXmasThisYear = xmasThisYear.atStartOfDay( earliestXmasZone );
Instant instantEarliestXmasThisYear = zdtEarliestXmasThisYear.toInstant();
Clock clockEarliestXmasThisYear = Clock.fixed( instantEarliestXmasThisYear , earliestXmasZone );
Use that special fixed clock to always return the same moment. We get the first moment of Christmas day in Kiritimati, with UTC showing a wall-clock time of fourteen hours earlier, 10 AM on the prior date of the 24th of December.
Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );
instant.toString(): 2016-12-24T10:00:00Z
zdt.toString(): 2016-12-25T00:00+14:00[Pacific/Kiritimati]
See live code in IdeOne.com.
True time, different time zone
You can control which time zone is assigned by the Clock
implementation. This might be useful in some testing. But I do not recommend this in production code, where you should always specify explicitly the optional ZoneId
or ZoneOffset
arguments.
You can specify that UTC be the default zone.
ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );
You can specify any particular time zone. Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
You can specify the JVM’s current default time zone should be the default for a particular Clock
object.
ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );
Run this code to compare. Note that they all report the same moment, the same point on the timeline. They differ only in wall-clock time; in other words, three ways to say the same thing, three ways to display the same moment.
System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );
America/Los_Angeles
was the JVM current default zone on the computer that ran this code.
zdtClockSystemUTC.toString(): 2016-12-31T20:52:39.688Z
zdtClockSystem.toString(): 2016-12-31T15:52:39.750-05:00[America/Montreal]
zdtClockSystemDefaultZone.toString(): 2016-12-31T12:52:39.762-08:00[America/Los_Angeles]
The Instant
class is always in UTC by definition. So these three zone-related Clock
usages have exactly the same effect.
Instant instantClockSystemUTC = Instant.now ( Clock.systemUTC () );
Instant instantClockSystem = Instant.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
Instant instantClockSystemDefaultZone = Instant.now ( Clock.systemDefaultZone () );
instantClockSystemUTC.toString(): 2016-12-31T20:52:39.763Z
instantClockSystem.toString(): 2016-12-31T20:52:39.763Z
instantClockSystemDefaultZone.toString(): 2016-12-31T20:52:39.763Z
Default clock
The implementation used by default for Instant.now
is the one returned by Clock.systemUTC()
. This is the implementation used when you do not specify a Clock
. See for yourself in pre-release Java 9 source code for Instant.now
.
public static Instant now() {
return Clock.systemUTC().instant();
}
The default Clock
for OffsetDateTime.now
and ZonedDateTime.now
is Clock.systemDefaultZone()
. See source code.
public static ZonedDateTime now() {
return now(Clock.systemDefaultZone());
}
The behavior of the default implementations changed between Java 8 and Java 9. In Java 8, the current moment is captured with a resolution only in milliseconds despite the classes’ ability to store a resolution of nanoseconds. Java 9 brings a new implementation able to capture the current moment with a resolution of nanoseconds – depending, of course, on the capability of your computer hardware clock.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?