Override Java System.currentTimeMillis to check timecode code

Is there a way, both in the code and in the JVM arguments, to override the current time represented through System.currentTimeMillis , besides manually changing the system clock on the main machine?

A bit of background:

We have a system that runs a series of accounting tasks that rotate most of their logic for the current date (i.e., the 1st of the month, the 1st of the year, etc.).

Unfortunately, many of the legacy codes call functions like new Date() or Calendar.getInstance() , both of which end up calling System.currentTimeMillis .

For testing purposes, right now we are stuck with manually updating the system clock to manipulate the time and date when the code thinks the test is running.

So my question is:

Is there a way to override what System.currentTimeMillis returns? For example, to tell the JVM to automatically add or subtract some offset before returning from this method?

Thanks in advance!

+99
java jvm testing systemtime
Jan 04 '10 at 19:39
source share
12 answers

I highly recommend that instead of messing around with the system clock, you bite a bullet and refactor this legacy code to use a replaceable clock. Ideally, this should be done using dependency injection, but even if you used a replaceable singleton, you would have the opportunity to test.

This can be almost automated by searching and replacing for the singleton version:

  • Replace Calendar.getInstance() with Clock.getInstance().getCalendarInstance() .
  • Replace new Date() with Clock.getInstance().newDate()
  • Replace System.currentTimeMillis() with Clock.getInstance().currentTimeMillis()

(etc. as needed)

After you have taken this first step, you can replace singleton with DI one by one.

+92
Jan 04 '10 at 19:43
source share

TL; dr

Is there a way, both in code and with JVM arguments, to override the current time represented through System.currentTimeMillis, besides 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 replacing interchangeable watches to facilitate testing using fake date-time. The java.time package in Java 8 includes the abstract class java.time.Clock with an explicit purpose:

to enable the inclusion of alternative hours if necessary

You can plug in your own implementation of Clock , although you can probably find one that has already been completed to suit your needs. For your convenience, java.time includes static methods for creating custom implementations. These alternative implementations may be useful during testing.

Altered cadence

Different tick… methods produce watches that increase the current moment with a different cadence.

By default, Clock reports time updated as often as milliseconds in Java 8, and in Java 9 as nanoseconds (depending on your hardware). You can request that the current current moment be reported with a different granularity.

False clock

Some watches may lie, creating a result different from the result of the hardware clock of the operating system.

  • fixed - Reports one unchanged (non-incremental) moment as the current moment.
  • offset - Reports the current moment, but is shifted by the Duration argument.

For example, a castle at 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 currently is Pacific/Kiritimati +14:00 to +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 this special fixed watch to always return the same moment. We get the first moment of Christmas day in Kiritimati , with UTC showing the wall clock fourteen hours ago, 10 a.m. on the previous date, December 24th.

500px-Kiribati_on_the_globe_% 28Polynesia_centered% 29.svg.png

 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 the code at IdeOne.com .

True time, different time zone

You can control which time zone is assigned by the Clock implementation. This may be useful with some tests. But I do not recommend this in production code, where you should always explicitly specify the optional ZoneId or ZoneOffset .

You can specify that UTC will be the default zone.

 ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () ); 

You can specify any time zone. Specify the correct time zone name in continent/region format, such as America/Montreal , Africa/Casablanca or Pacific/Auckland . Never use an abbreviation of 3-4 letters, such as EST or IST since they are not real time zones, not standardized, and not even unique (!).

 ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) ); 

You can specify the JVM, the current default time zone should be the default for a specific Clock object.

 ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () ); 

Run this code for comparison. 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. Thus, these three associated with the Clock zone are used in exactly the same way.

 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 default implementation for Instant.now is the one returned by Clock.systemUTC() . This is the implementation used when you do not specify Clock . See for yourself in the preliminary release of Java 9 source code for Instant.now .

 public static Instant now() { return Clock.systemUTC().instant(); } 

Clock default Clock for OffsetDateTime.now and ZonedDateTime.now is Clock.systemDefaultZone() . See Source Code .

 public static ZonedDateTime now() { return now(Clock.systemDefaultZone()); } 

The default implementation behavior has changed between Java 8 and Java 9. In Java 8, the current moment is fixed with a resolution of only milliseconds, despite the ability of classes to preserve nanosecond resolution. Java 9 brings a new implementation that can capture the current moment with a resolution of a nanosecond - depending, of course, on the capabilities of your computer clock.




About java.time

The java.time framework is built into Java 8 and later. These classes supersede the nasty old obsolete time classes such as java.util.Date , Calendar and SimpleDateFormat .

The Joda-Time project, now in maintenance mode , advises switching to the java.time classes.

To learn more, check out the Oracle tutorial . And search for qaru for many examples and explanations. The specification is JSR 310 .

You can exchange java.time objects directly with your database. Use a JDBC driver compatible with JDBC 4.2 or later. No strings needed, no java.sql.* Needed.

Where can I get java.time classes?

  • Java SE 8 , Java SE 9 and later
    • Built in.
    • Part of the standard Java API with integrated implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Most of the functionality of java.time is included back in Java 6 and 7 in ThreeTen-Backport .
  • Android
    • Later versions of the Android package implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts the ThreeTen-Backport (mentioned above). See How to use ThreeTenABP ....

The ThreeTen-Extra project extends java.time with additional classes. This project is a proof of possible future additions to java.time. Here you can find useful classes such as Interval , YearWeek , YearQuarter and others .

+48
Oct 26 '14 at 7:54
source share

As John Skeet said :

"Use Joda time" is almost always the best answer to any question related to "how can I achieve X using java.util.Date/Calendar?"

So here (suppose you just replaced all new Date() with new DateTime().toDate() )

 //Change to specific time DateTimeUtils.setCurrentMillisFixed(millis); //or set the clock to be a difference from system time DateTimeUtils.setCurrentMillisOffset(millis); //Reset to system time DateTimeUtils.setCurrentMillisSystem(); 

If you want to import a library with an interface (see John's comment below), you can simply use Prevayler Clock , which will provide an implementation as well as a standard interface. A full can is only 96 KB, so it should not break the bank ...

+40
Jan 04 '10 at 19:48
source share

When using some DateFactory template it seems nice, it does not cover libraries that you cannot control - submit a check annotation. @Past with an implementation based on System.currentTimeMillis (there is one).

This is why we use jmockit to directly mock system time:

 import mockit.Mock; import mockit.MockClass; ... @MockClass(realClass = System.class) public static class SystemMock { /** * Fake current time millis returns value modified by required offset. * * @return fake "current" millis */ @Mock public static long currentTimeMillis() { return INIT_MILLIS + offset + millisSinceClassInit(); } } Mockit.setUpMock(SystemMock.class); 

Since it is not possible to get the original uncommitted millis value, instead we use a nano timer - this is not related to the wall clock, but relative time is enough here:

 // runs before the mock is applied private static final long INIT_MILLIS = System.currentTimeMillis(); private static final long INIT_NANOS = System.nanoTime(); private static long millisSinceClassInit() { return (System.nanoTime() - INIT_NANOS) / 1000000; } 

There is a documented problem: when using HotSpot, time returns to normal after several calls - here is the problem report: http://code.google.com/p/jmockit/issues/detail?id=43

To overcome this, we need to enable one specific HotSpot optimization - start the JVM with this argument -XX:-Inline .

Although this may not be ideal for production, it is just great for tests, and it is completely transparent to applications, especially when DataFactory does not make business sense and is introduced only because of tests. It would be nice to have the JVM built-in ability to run at different times, too bad that this is impossible without such hacks.

Full story on my blog: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/

The message contains a complete set of SystemTimeShifter. The class can be used in your tests, or it can be used as the first main class until your real main class is very easy to start the application (or even the whole application server) at another time. Of course, this is intended for testing purposes mainly, and not for the production environment.

EDIT July 2014: JMockit has changed a lot lately and you have to use JMockit 1.0 for proper use (IIRC). Definitely cannot upgrade to a new version where the interface is completely different. I thought about investing only the necessary things, but since we do not need this in our new projects, I do not develop this thing at all.

+12
Jan 02 '13 at 10:05
source share

Powermock works great. Just used it to make fun of System.currentTimeMillis() .

+7
Apr 03 '10 at 18:27
source share

Use aspect-oriented programming (AOP, such as AspectJ) to weave the System class to return a predefined value that you could set in your test cases.

Or drag and drop application classes to redirect the call to System.currentTimeMillis() or new Date() to another native class of its own.

However, the system weaving classes ( java.lang.* ) Are a bit more complicated, and you may need to do stand-alone weaving for rt.jar and use separate JDK / rt.jar for your tests.

It is called Binary weaving , as well as special tools for weaving classes of the system and bypassing some problems with the help of which (for example, rebooting VM may not work)

+5
Jan 04 '10 at 19:49
source share

Actually, there is no way to do this directly on the virtual machine, but you can program anything on the system time on the test machine. Most (all?) OSs have command line commands for this.

+3
Jan 04 '10 at 19:43
source share

Without repeated factoring, can you test the launch in an instance of the OS virtual machine?

+3
Jan 04 '10 at 21:18
source share

In my opinion, only a non-invasive solution can work. Especially if you have external libraries and a large database of legacy codes, there is no reliable way to spend time.

JMockit ... only works for a limited number of times

PowerMock and Co ... should mock clients in System.currentTimeMillis (). Again an invasive option.

From this, I see that the javaagent or aop approach mentioned is transparent to the whole system. Has anyone done this and could point to such a decision?

@jarnbjo: could you show javaagent code?

+2
Jun 23 2018-12-12T00:
source share

A working way to override the current system time for JUnit testing purposes in a Java 8 web application using EasyMock without Joda time and without PowerMock.

Here is what you need to do:

What you need to do in the test class

Step 1

Add the new java.time.Clock attribute to the MyService class being tested and make sure that the new attribute is correctly initialized by default with the help of the instantiation block or constructor:

 import java.time.Clock; import java.time.LocalDateTime; public class MyService { // (...) private Clock clock; public Clock getClock() { return clock; } public void setClock(Clock newClock) { clock = newClock; } public void initDefaultClock() { setClock( Clock.system( Clock.systemDefaultZone().getZone() // You can just as well use // java.util.TimeZone.getDefault().toZoneId() instead ) ); } { initDefaultClock(); // initialisation in an instantiation block, but // it can be done in a constructor just as well } // (...) } 

Step 2

Introduce the new clock attribute into the method that calls the current date-time. For example, in my case, I had to check to see if the date stored in dataase occurred before LocalDateTime.now() , which I moved using LocalDateTime.now(clock) , for example:

 import java.time.Clock; import java.time.LocalDateTime; public class MyService { // (...) protected void doExecute() { LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB(); while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) { someOtherLogic(); } } // (...) } 

What you need to do in the test class

Step 3

In the test class, create a mock clock object and enter it into the tested instance of the class just before you call the proven doExecute() method, then reset it will appear again like this:

 import java.time.Clock; import java.time.LocalDateTime; import java.time.OffsetDateTime; import org.junit.Test; public class MyServiceTest { // (...) private int year = 2017; private int month = 2; private int day = 3; @Test public void doExecuteTest() throws Exception { // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot MyService myService = new MyService(); Clock mockClock = Clock.fixed( LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()), Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId() ); myService.setClock(mockClock); // set it before calling the tested method myService.doExecute(); // calling tested method myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method // (...) remaining EasyMock stuff: verify(..) and assertEquals(..) } } 

Check it in debug mode, and you will see that the February 3, 2017 date was correctly entered into the MyService instance and used in the comparison instruction, and then it was correctly reset to the current date using initDefaultClock() .

+2
Aug 21 '17 at 14:46
source share

If you are using Linux, you can use the main libfaketime branch or during commit 4ce2835 testing.

Just set the environment variable over time when you want to mock your java application and run it using ld-preloading:

 # bash export FAKETIME="1985-10-26 01:21:00" export DONT_FAKE_MONOTONIC=1 LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1 java -jar myapp.jar 

The second environment variable is of utmost importance to Java applications that would otherwise freeze. This requires the main libfaketime branch at the time of writing.

If you want to change the running time of the systemd managed service, simply add the following to your file file overrides, for example. for elasticsearch it will be /etc/systemd/system/elasticsearch.service.d/override.conf :

 [Service] Environment="FAKETIME=2017-10-31 23:00:00" Environment="DONT_FAKE_MONOTONIC=1" Environment="LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1" 

Remember to reboot systemd with `systemctl daemon-reload

+1
Nov 03 '17 at 9:41 on
source share

If you want to make fun of a method with the argument System.currentTimeMillis() , then you can pass anyLong() the Matchers class as an argument.

PS I can successfully run my test case using the aforementioned trick and just share more detailed information about my test, which I use in PowerMock and Mockito frameworks.

-one
Aug 12 '15 at 10:12
source share



All Articles