Changing system time in Java

I always wanted to find some reasonable way how to tell JVM “hey, it’s 3:47 AM, January the first, 2000”. When you have a need like this you probably use some DateFactory pattern. People argue – “that’s the cleanest way, put it into the design!” However this comes with a price:

  • This cleanest way means to pollute your code on many, many places with your DateFactory – one place where a programmer forgets and you’re screwed. No need to mention how difficult to trace these problems may be, though they are not difficult to locate when you find out what the problem is – because there is only one place where new Date() and/or System.currentTimeMillis() may appear – in your DateFactory class. (Now when I think of it, I’d probably come up with a technical solution to force this pattern, some Java language aware analysis tool searching for exactly these two calls.)
  • This DateFactory may or may not be dictated by the requirements – real, inherent ones, not derived from technical needs…
  • …actually, in many cases you need to shift time only because of the tests.

When the application somehow implies DateFactory solution, so be it. There surely are systems where it makes sense. In my line of work, however, I messed with some kind of the DateFactory only because of the need to test the system on a specific date, or (in rare cases) to run some highly administrative feature as if it ran on a specific date. While the second case could be just right for the DateFactory, it wasn’t part of our main application code, but rather our migration/preparation/installation utilities and they were never run by the user. So if I had known about any other solution than DateFactory, I’d have gladly welcomed it. I hadn’t then.

Every single time this situation occurred I cursed JVM creators for not adding unified standard way how to mess with system time (guarded by security manager just in case), or some “java” command option, or whatever. My need arises again and again – especially during testing. It’s much harder to prepare time aware data (you have to generate/customize them every time) then declare “you run at this time!” Unless the second thing is impossible of course. But imagine you need to test specific billing job running on New Year’s Day. “Go for the DateFactory you moron!” Yeah, but imagine you have system with @Past annotation from Bean Validation framework. Guess how that one uses your DateFactory? Finally I got the idea – to mock it somehow. Mocking such a thing though is messing with JVM to a degree – as you will see later. Now let’s follow the story as it went…

Solution: jmockit

Actually – I wasn’t the first one. I found that other people wanted to use jmockit library to mock the system time. Jmockit is surely powerful library, more powerful than I can tell, because I actually (sorry to say that) suck in mocking. If you mock System.currentTimeMillis, you’re done (new Date() and all other ways to get the real time use this call). But how to mock this system static native call? Jmockit can do it, and it does it on the side of the callee – which means you don’t need to modify anything else – just set up the mock on the side of System class. During some troubleshooting I tried to use different mock libraries and different approaches, but unless I’m mistaken there are only two ways how to do it – on the caller’s side – or on the callee’s one. Callee is the right side, right? The same way how you have to pollute your code with DateFactory call, you’d have to enumerate, autoscan or whatever else your code in order to mock it on the caller’s side. No way to do this simply and completely when you think about dynamic class loading too and I bet you can throw many other problems on this solution. So I reiterate: *Mock the system time on the callee’s side.* (That is System class, obviously.) Jmockit can do just that.

First we need the class “overriding” currentTimeMillis – here it is (it can be nested class BTW, that’s why it’s static on my listing):

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();
       }
   }

And now we will use this class:

import mockit.Mockit;
...
           Mockit.setUpMock(SystemMock.class);

Done! From now on all time is changed for the JVM. Or is it? This solution actually has two problems. Let’s deal with the problem numero uno:

Problem 1: you can’t call the original currentTimeMillis

How can you base your shifted time on the real time, when you don’t have the original time now? It would be nice if you just could set the offset and your method simply returned non-mocked millis + the offset. Jmockit can mock the call, but then it is not possible to call the original method without the mock – not for static native calls. So if you use it, you’ll run out of stack in recursion gone wild.

Luckily since Java 5 we have System.nanoTime(). While nanoTime itself is not based on any real world time, we can still use it to determine the time since we set up the mock – if we remember what were nanoTime and currentTimeMillis before we did so – and it can be any particular time, for instance class initialization time:

   private static final long INIT_MILLIS = System.currentTimeMillis();
   private static final long INIT_NANOS = System.nanoTime();

Now it’s easy to tell how might method called from my mock method look like:

   private static long millisSinceClassInit() {
       return (System.nanoTime() - INIT_NANOS) / 1000000;
   }

The only thing you need to do now is to set offset in some meaningful way. I offer my whole solution wrapped in a class straightforwardly called SystemTimeShifter:

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import mockit.Mock;
import mockit.MockClass;
import mockit.Mockit;

/**
 * Class changes the system time returned by {@link System#currentTimeMillis()} via JMockit weaving.
 * <p/>
 * Original System class can be restored any time calling {@link #reset()} method. There are a few ways how to specify modified system time:
 * <ul>
 * <li>setting ms offset via {@link #setOffset(long)}
 * <li>changing ms offset (relatively) via {@link #changeOffset(long)}
 * <li>setting new date, time or ISO date/time via {@link #setIsoDate(String)}
 * </ul>
 * <p/>
 * Any of these methods can be used through system properties (-D) this way (first property in this order is used, others ignored):
 * <ul>
 * <li>{@code -Dsystime.offset=1000} - shift by one second to the future (negative number can be used)
 * <li>{@code -Dsystime.millis=1000} - set system time one second after start of the era (1970...)
 * <li>{@code -Dsystime.iso=2000-01-01T00:00:47} - 47 seconds after beginning of the 2000, alternatively you can set only time (00:00:47, date stays current) or
 * only date (2000-01-01, current time) without 'T' in both cases.
 * </ul>
 * <p/>
 * There must be something that causes class load, otherwise nothing happens. In order to allow this without modifying the original program, one may use this
 * class as a main class with original main class as the first argument (they will be correctly shifted when served to the original class). If no relevant
 * property is specified via -D, nothing happens. In any case (programmatic or main class replacement) this class has to be on a classpath. For application
 * server usage this means it has to be in its system libraries, not in EAR/WAR that is not loaded during the AS start yet.
 * <p/>
 * Example:
 * 
 * <pre>
 * java -Dsystime.iso=2000-01-01T00:00:47 SystemTimeShifter my.uber.appserver.Main arg1 second "third long with spaces"
 * </pre>
 * <b>WARNING:</b> Sun/Oracle HotSpot JVM and its inline optimization may mess up with the mock after it is set up, so if you notice that the time
 * returns to normal after number of invocations, you should add {@code -XX:-Inline} option to your java command line. Other JVM specific options
 * may be needed for different JVM implementations.
 * 
 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a>
 */
public class SystemTimeShifter {
	/**
	 * System property setting ms offset.
	 */
	public static final String PROPERTY_OFFSET = "systime.offset";

	/**
	 * System property setting "current" millis.
	 */
	public static final String PROPERTY_MILLIS = "systime.millis";

	/**
	 * System property setting ISO date/time (or date, or time).
	 */
	public static final String PROPERTY_ISO_DATE = "systime.iso";

	private static final long INIT_MILLIS = System.currentTimeMillis();
	private static final long INIT_NANOS = System.nanoTime();
	private static long offset;

	private static boolean mockInstalled;

	@Deprecated
	protected SystemTimeShifter() {
		// prevents calls from subclass
		throw new UnsupportedOperationException();
	}

	static {
		String isoDate = System.getProperty(PROPERTY_ISO_DATE);
		String millis = System.getProperty(PROPERTY_MILLIS);
		String offset = System.getProperty(PROPERTY_OFFSET);
		try {
			if (isoDate != null) {
				setIsoDate(isoDate);
			} else if (millis != null) {
				setMillis(Integer.parseInt(millis));
			} else if (offset != null) {
				setOffset(Integer.parseInt(offset));
			}
		} catch (NumberFormatException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Bootstrap main to allow time shifting before actually loading the real main class. Real
	 * main class must be the first argument, it will be removed from the list when calling the
	 * real class. Without using any relevant -D property there will be no time shifting.
	 * 
	 * @param args argument list with original (desired) class as the first argument
	 * @throws Exception may happen during the reflection call of the other main
	 */
	@SuppressWarnings({"unchecked", "rawtypes"})
	public static void main(String[] args) throws Exception {
		String[] newArgs = new String[args.length - 1];
		System.arraycopy(args, 1, newArgs, 0, args.length - 1);

		Class clazz = Class.forName(args[0]);
		Method main = clazz.getMethod("main", newArgs.getClass());
		main.invoke(null, (Object) newArgs);
	}

	/**
	 * Sets the new "system" time to specified ISO time. It is possible to set exact time with the format {@code yyyy-MM-dd'T'HH:mm:ss} (no apostrophes around T
	 * in the actual string!) or one can set just time
	 * (then current date stays) or just date (then current time stays).
	 * <p/>
	 * If parse fails for whatever reason, nothing is changed.
	 * 
	 * @param isoDate String with ISO date (date+time, date or just time)
	 */
	public static synchronized void setIsoDate(String isoDate) {
		try {
			if (isoDate.indexOf('T') != -1) { // it's date and time (so "classic" ISO timestamp)
				long wantedMillis = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(isoDate).getTime();
				offset = wantedMillis - millisSinceClassInit() - INIT_MILLIS;
			} else if (isoDate.indexOf(':') != -1) { // it's just time we suppose
				Calendar calx = Calendar.getInstance();
				calx.setTime(new SimpleDateFormat("HH:mm:ss").parse(isoDate));

				Calendar cal = Calendar.getInstance();
				cal.set(Calendar.HOUR_OF_DAY, calx.get(Calendar.HOUR_OF_DAY));
				cal.set(Calendar.MINUTE, calx.get(Calendar.MINUTE));
				cal.set(Calendar.SECOND, calx.get(Calendar.SECOND));
				offset = cal.getTimeInMillis() - millisSinceClassInit() - INIT_MILLIS;
			} else { // it must be just date then!
				Calendar calx = Calendar.getInstance();
				calx.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(isoDate));

				Calendar cal = Calendar.getInstance();
				cal.set(Calendar.DAY_OF_MONTH, calx.get(Calendar.DAY_OF_MONTH));
				cal.set(Calendar.MONTH, calx.get(Calendar.MONTH));
				cal.set(Calendar.YEAR, calx.get(Calendar.YEAR));
				offset = cal.getTimeInMillis() - millisSinceClassInit() - INIT_MILLIS;
			}
			mockSystemClass();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Sets ms offset against current millis (not against real, instead changes current value relatively).
	 * 
	 * @param offset relative ms offset against "current" millis
	 */
	public static synchronized void changeOffset(long offset) {
		SystemTimeShifter.offset += offset;
		mockSystemClass();
	}

	/**
	 * Sets ms offset against real millis (rewrites previous value).
	 * 
	 * @param offset new absolute ms offset against real millis
	 */
	public static synchronized void setOffset(long offset) {
		SystemTimeShifter.offset = offset;
		mockSystemClass();
	}

	/**
	 * Sets current millis to the specified value.
	 * 
	 * @param timestamp new value of "current" millis
	 */
	public static synchronized void setMillis(long timestamp) {
		offset = timestamp - INIT_MILLIS;
		mockSystemClass();
	}

	/**
	 * Resets the whole System time shifter and removes all JMockit stuff. Real system call is restored.
	 */
	public static synchronized void reset() {
		Mockit.tearDownMocks(System.class);
		mockInstalled = false;
		offset = 0;
		System.out.println("Current time millis mock REMOVED");
	}

	private static void mockSystemClass() {
		if (!mockInstalled) {
			Mockit.setUpMock(SystemMock.class);
			System.out.println("Current time millis mock INSTALLED: " + new Date());
			mockInstalled = true;
		} else {
			System.out.println("Current time millis mock probably INSTALLED previously: " + new Date());
		}
	}

	public static boolean isMockInstalled() {
		return mockInstalled;
	}

	/**
	 * Handy if you set up the mock by some other means like {@link Mockit#setUpStartupMocks(Object...)}.
	 *
	 * @param mockInstalled true if you want to pretend that the mock is already in place (or is/will be installed otherwise)
	 */
	public static void setMockInstalled(boolean mockInstalled) {
		SystemTimeShifter.mockInstalled = mockInstalled;
	}

	/**
	 * Returns real time millis based on nano timer difference (not really a call to {@link System#currentTimeMillis()}.
	 * 
	 * @return real time millis as close as possible
	 */
	public static long currentRealTimeMillis() {
		return INIT_MILLIS + millisSinceClassInit();
	}

	private static long millisSinceClassInit() {
		return (System.nanoTime() - INIT_NANOS) / 1000000;
	}

	@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();
		}
	}
}

I’m not suggesting it’s a masterpiece of engineering – but it works pretty well and you can set up the offset in many handy ways (just the date/time, both, offset in ms, you name it), you can use it as the main class before you start some other real main class (this way I started the whole application server in completely different time – Weblogic 12c in my case, but I think it doesn’t really matter). But I did mention two problems, right?

Problem 2: After some time the time returns to normal!

This problem quickly manifests on Sun’s JVM (can’t tell about others) and I found this non-solution: http://code.google.com/p/jmockit/issues/detail?id=43

At least originally it was no-go for me as the suggested solution was -Xint – using interpreted JVM. This made tests unbearably slow, nobody would run them! I felt I’m pretty close to the solution and thanks to Rogerio Liesenfeld (jmockit author) and my private conversation with him above this issue I got pushed into the right direction. Sure, first I tried other mocking libs, but then I got back to jmockit (because it mocks the target, not the source of the call) and I tried again. I focused on what Rogerio wrote in his comment #7 (which was actually his reply to my mail). “So OK, if it’s HotSpot optimization, can’t I disable just that one? Even for that single class?” While I could not disable it for the class with Sun’s HotSpot (and I don’t know if it is any problem at all on different JVMs) I could disable it globally with -XX:-Inline. And yes, it’s much much faster than -Xint. Generally it should not be much slower than 1.5x of normal run with inlining – this depends on many factors of course (on overall “inline-likeness” of your application/test), but it’s much better than typical 10x penalty for interpreted JVM.

Problem 3: TestNG integration

Ok, I lied about two problems only – but only because this one is beyond the system time shifter itself. This problem was caused by my lack of understanding of jmockit and its interaction with TestNG. I wanted to shift my time for the whole TestNG suite without realizing that jmockit/TestNG may have their own agendas too. I tried to run my code with @Before/AfterSuite setting up my mocked time with SystemTimeShifter.setIsoDate(…) call and tearing it down again with reset(). But it didn’t work inside the test methods anyway, because something uninstalled my mock before the test method was run. Cedric from TestNG told me it’s jmockits business and Rogerio told me I’m using it the wrong way because it was never meant to be run this way for the whole Suite. I could use it around class or even method call, but I didn’t like the idea of time being shifted to the same point for every single method. I didn’t like the idea, that I can’t tell from log files how long the test is running (though the log file times were shifted too, I hope you don’t mind :-)). Silly on my part, however, this should work for you just fine:

   @BeforeSuite
   public final void setupSystemTime() {
       SystemTimeShifter.setMockInstalled(true); // because of the setUpStartupMocks further, which sets up the mock
       SystemTimeShifter.setIsoDate("2011-10-14T10:00:00"); // this set's the offset - but doesn't set up the mock (thanks to the assert above)

       // this line performs all the suite scope magic
       Mockit.setUpStartupMocks(SystemTimeShifter.SystemMock.class);
   }

   @AfterSuite
   public final void resetSystemTime() {
       SystemTimeShifter.reset();
   }

While technically the AfterSuite part is not necessary, it is cleaner this way. Jmockit uninstalls his part by itself, but time shifter doesn’t know about it. This way it got reset as well.

Conclusion

There it is – not perfect and not without little hassles (-XX:-Inline namely), but working fine so far, at least for my cases. In my eyes – this is really tragic fault of the current JVM date library, fault that cannot be mended easily. System.currentTimeMillis is the only relevant source of real time and unless we can mess with it somehow in a clear way we will always have need to mess with it in ugly ways. This reminds me of the t-shirt I have. There is a stargate like ring on it, it is just named “TIME GATE” – and Java Duke comes out of it, opening the future for us. I’m curious a bit how he could do it. Did he cheat on JVM somehow too? When will we have clean solution that makes any DateFactory rather obsolete? Will we ever? Or do we have to unlock the future with TimeShifters in such obscure ways? Java, Java…

Let me know your story with system time cheating. I know I haven’t talked about setting the computer system time. That is simplest solution for many occasions, but not for running the tests on Continuous Integration server shared with many other projects, so I just omitted this altogether. If you have any success story with SystemTimeShifter, if it helps you somehow, please, be so kind and let me know to lift up my spirit. 🙂

8 thoughts on “Changing system time in Java

  1. Thank you, this helped me a lot when creating testng test, where we needed to change the system time.

    1. I don’t understand what you need. SystemTimeShifter (expand the source at the end of the Problem 1 part) has a few public static set* methods, just use them. In Problem 3 part there is a snippet of code usable from test.

Leave a comment