4

The clock accuracy on Windows seems to have changed lately. I remember it being very accurate since Windows 7 (1 ms resolution), and now the time hops in steps of 15 to 16 ms. I noticed due to some (albeit poorly written) unit tests failing (tests checked that some time elapsed between writing and reading records).

This affects following implementations:

  • System.currentTimeMillis()
  • LocalTime.now()
  • LocalDateTime.now()
  • ZonedDateTime.now()

I'm well aware that elapsed time is to be measured using System.nanoTime() deltas, but I'm wondering nevertheless if I missed something that changed back the clock resolution to 15-16ms.

Environment:

  • Windows 10 version 1709
  • JRE/JDK 1.8.0_171-b11 (64-bit)

Did anyone notice this as well? What could be the reason for this resolution change?

EDIT:
Test code to check this behaviour (see output: time changes and number of samples before change)

@Test
public void testCurrentTimeMillis() {
    test(() -> System.currentTimeMillis());
}

@Test
public void testNanoTime() {
    test(() -> System.nanoTime());
}

@Test
public void testLocalTime() {
    test(() -> LocalTime.now());
}

@Test
public void testLocalDateTime() {
    test(() -> LocalDateTime.now());
}

@Test
public void testZonedDateTime() {
    test(() -> ZonedDateTime.now());
}

private <T> void test(Supplier<T> timeSupplier) {

    int samples = 20;
    String lastTimeString = null;
    int count = 0;
    while (samples > 0) {

        count++;
        String timeString = timeSupplier.get().toString();
        if (!timeString.equals(lastTimeString)) {
            System.out.println(timeString + " (" + count + ")");
            lastTimeString = timeString;
            count = 0;
            samples--;
        }
    }
}
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Peter Walser
  • 15,208
  • 4
  • 51
  • 78
  • Do you have windows developer mode on? – Shanu Gupta Apr 25 '18 at 13:14
  • @ShanuGupta: no, it's off (on 'Sideload Apps') – Peter Walser Apr 25 '18 at 13:18
  • Can you turn on and check again? You might have to restart system. – Shanu Gupta Apr 25 '18 at 13:19
  • Possible duplicate of [Timer accuracy in java](https://stackoverflow.com/questions/1245388/timer-accuracy-in-java) – Ole V.V. Apr 25 '18 at 13:31
  • @OleV.V. the other question dates back to 2009, back then such inacurracies were common on 32-bit Windows platforms. My case is a recent 64-bit Windows platform, where the problem resurfaced. – Peter Walser Apr 25 '18 at 13:37
  • @ShanuGupta developer mode activated & restarted - same effect. – Peter Walser Apr 25 '18 at 13:41
  • I’ve searched a bit around in vain. You can probably do better. Anyway IIRC, I read recently that this is a Windows issue (in 2018 still) and therefore not something that can be solved in Java alone. There are some tricks to persuade Windows to giving a better resolution, but they are expensive in power consumption, so you should use them with care if at all. There is also a JDK bug open that aims at better accuracy on Windows. Disclaimer: as I said, it’s all from fragile memory. – Ole V.V. Apr 25 '18 at 13:47
  • Just ran the test code on a collegue's machine - he's getting 1ms resolutions (same machine build, windows version, only the latest windows security updates KBID 4093110,4099989,4093112 from 2018-04-11 not installed). Maybe the resolution has been lowered as a countermeasure to SPECTRE vulnerabilities? – Peter Walser Apr 25 '18 at 13:57

1 Answers1

1

To get the local time with the best accuracy regardless of the platform, I came up with a solution with is based on the current time (measuring when it is increased) as an anchor, and and offset based on System.nanoTime() deltas.

Advantages:

  • current time with precision not depending on the platform
  • high accuracy (nano time)
  • portable (as the java.time objects can actually carry nanosecond precision).

Code (can be adapted to serve LocalTime and LocalDateTime as well):

/**
 * Exact zoned date/time, compensates for the 15-16ms leaps 
 * of milliseconds time on some Java platforms.
 */
public final class ExactZonedDateTime {

    private static ZonedDateTime anchor;
    private static long anchorNanos;

    static {
        ZonedDateTime last = ZonedDateTime.now();
        while (((anchor = ZonedDateTime.now()).equals(last))) {
            anchorNanos = System.nanoTime();
        }
    }

    private ExactZonedDateTime() {
    }

    public static ZonedDateTime now() {
        return anchor.plusNanos(System.nanoTime() - anchorNanos);
    }
}

Subsequent calls:

2018-04-26T12:51:02.293079632+02:00[Europe/Zurich] 2018-04-26T12:51:02.293524865+02:00[Europe/Zurich] 2018-04-26T12:51:02.293598126+02:00[Europe/Zurich] 2018-04-26T12:51:02.293660770+02:00[Europe/Zurich] 2018-04-26T12:51:02.293725538+02:00[Europe/Zurich]

Peter Walser
  • 15,208
  • 4
  • 51
  • 78