< prev index next >

jdk/test/java/time/test/java/time/TestClock_System.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.

@@ -60,11 +60,13 @@
 package test.java.time;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertSame;
 
+import java.lang.reflect.Field;
 import java.time.Clock;
+import java.time.Instant;
 import java.time.ZoneId;
 
 import org.testng.annotations.Test;
 
 /**

@@ -85,6 +87,300 @@
     public void test_toString() {
         Clock test = Clock.system(PARIS);
         assertEquals(test.toString(), "SystemClock[Europe/Paris]");
     }
 
+    //-----------------------------------------------------------------------
+
+    private static String formatTime(String prefix, Instant time) {
+        return prefix + ": " + time + " - seconds: "
+                        + time.getEpochSecond() + ", nanos: "
+                        + time.getNano();
+    }
+
+    public void test_ClockResolution() {
+        Clock highestUTC = Clock.systemUTC();
+
+        Instant start = Instant.ofEpochMilli(System.currentTimeMillis());
+
+        try {
+            // smoke test
+            Instant system1 = Instant.ofEpochMilli(System.currentTimeMillis());
+            Instant system2 = Instant.ofEpochMilli(System.currentTimeMillis());
+            Instant highest1 = highestUTC.instant();
+            Instant highest2 = highestUTC.instant();
+            System.out.println(formatTime("\nsystemUTC #1            ", system1));
+            System.out.println(formatTime("systemUTC #2            ", system2));
+            System.out.println(formatTime("highestResolutionUTC #1 ", highest1));
+            System.out.println(formatTime("highestResolutionUTC #2 ", highest2));
+
+            if (system2.isBefore(system1)) {
+                System.err.println("system2 is before system1!");
+                System.err.println(formatTime("\n\tsystem1", system1));
+                System.err.println(formatTime("\n\tsystem2", system2));
+                throw new RuntimeException("system2 is before system1!"
+                        + formatTime("\n\tsystem1", system1)
+                        + formatTime("\n\tsystem2", system2));
+            }
+            if (highest2.isBefore(highest1)) {
+                System.err.println("highest2 is before highest1!");
+                System.err.println(formatTime("\n\thighest1", system1));
+                System.err.println(formatTime("\n\tsystem2", highest2));
+                throw new RuntimeException("highest2 is before system1!"
+                        + formatTime("\n\thighest1", system1)
+                        + formatTime("\n\tsystem2", highest2));
+            }
+
+            // better test - but depends on implementation details.
+            // we're not rounding - so highest1 should be greater or equal to
+            // system1
+            system1 = Instant.ofEpochMilli(System.currentTimeMillis());
+            highest1 = highestUTC.instant();
+
+            System.out.println(formatTime("\nsystemUTC            ", system1));
+            System.out.println(formatTime("highestResolutionUTC ", highest1));
+
+            if (highest1.isBefore(system1)) {
+                System.err.println("highest1 is before system1!");
+                System.err.println(formatTime("\n\tsystem1", system1));
+                System.err.println(formatTime("\n\thighest1", highest1));
+                throw new RuntimeException("highest1 is before system1!"
+                        + formatTime("\n\tsystem1", system1)
+                        + formatTime("\n\thighest1", highest1));
+            }
+
+            int count=0;
+            // let's preheat the system a bit:
+            for (int i = 0; i < 1000 ; i++) {
+                system1 = Instant.ofEpochMilli(System.currentTimeMillis());
+                highest1 = highestUTC.instant();
+                final int sysnan = system1.getNano();
+                final int nanos = highest1.getNano();
+                if ((nanos % 1000000) > 0) {
+                    count++; // we have micro seconds
+                }
+                if ((sysnan % 1000000) > 0) {
+                    throw new RuntimeException("Expected only millisecconds "
+                            + "precision for systemUTC, found "
+                            + (sysnan % 1000000) + " remainder.");
+                }
+            }
+            System.out.println("\nNumber of time stamps which had better than"
+                    + " millisecond precision: "+count+"/"+1000);
+            System.out.println(formatTime("\nsystemUTC            ", system1));
+            System.out.println(formatTime("highestResolutionUTC ", highest1));
+            if (count == 0) {
+                System.err.println("Something is strange: no microsecond "
+                        + "precision with highestResolutionUTC?");
+                throw new RuntimeException("Micro second preccision not reached");
+            }
+
+            // check again
+            if (highest1.isBefore(system1)) {
+                System.err.println("highest1 is before system1!");
+                System.err.println(formatTime("\n\tsystem1", system1));
+                System.err.println(formatTime("\n\thighest1", highest1));
+                throw new RuntimeException("highest1 is before system1!"
+                        + formatTime("\n\tsystem1", system1)
+                        + formatTime("\n\thighest1", highest1));
+            }
+
+            // leap of faith: ensure that highest1 is from within 10 secs of
+            //   system1
+            if (highest1.toEpochMilli() != system1.toEpochMilli()) {
+                long delta = highest1.getEpochSecond() - system1.getEpochSecond();
+                if (delta > 10) {
+                    throw new RuntimeException("Unexpected long delay between two clocks ("
+                            + delta + " seconds)"
+                            + formatTime("\n\t system1", system1)
+                            + formatTime("\n\t highest1", highest1));
+
+                }
+            } else {
+                System.out.println("You won the lottery: the two dates are within 1 millisecond!\n");
+            }
+
+        } finally {
+            Instant stop = Instant.ofEpochMilli(System.currentTimeMillis());
+            if (start.isAfter(stop)) {
+                // This should not happen - but can (un)probably be observed
+                // when switching to summer time, or if another application
+                // is switching the system date...
+                System.err.println("Cannot test - date was setback: "
+                        + formatTime("\n\tstarted at", start)
+                        + formatTime("\n\tstopped at", stop) + "\n");
+                return; // will prevent exceptions from being propagated.
+            }
+        }
+    }
+
+    static final long MAX_OFFSET = 0x0100000000L;
+    static final long MIN_OFFSET = -MAX_OFFSET;
+
+    // A helper class to test that SystemClock correctly recomputes
+    // its offset.
+    static class SystemClockOffset {
+
+        static final int MILLIS_IN_SECOND = 1000;
+        static final int NANOS_IN_MILLI = 1000_000;
+        static final int NANOS_IN_MICRO = 1000;
+        static final int NANOS_IN_SECOND = 1000_000_000;
+
+        static final boolean verbose = true;
+        static final Clock systemUTC = Clock.systemUTC();
+        static final Field offsetField;
+
+        static {
+            try {
+                offsetField = Class.forName("java.time.Clock$SystemClock").getDeclaredField("offset");
+                offsetField.setAccessible(true);
+            } catch (ClassNotFoundException | NoSuchFieldException ex) {
+                throw new ExceptionInInitializerError(ex);
+            }
+        }
+
+        static enum Answer {
+
+            YES, // isOffLimit = YES:   we must get -1
+            NO, // isOffLimit = NO:    we must not not get -1
+            MAYBE  // isOffLimit = MAYBE: we might get -1 or a valid adjustment.
+        };
+
+        static long distance(long one, long two) {
+            return one > two ? Math.subtractExact(one, two)
+                    : Math.subtractExact(two, one);
+        }
+
+        static Answer isOffLimits(long before, long after, long offset) {
+            long relativeDistanceBefore = distance(before, offset);
+            long relativeDistanceAfter = distance(after, offset);
+            if (relativeDistanceBefore >= MAX_OFFSET && relativeDistanceAfter >= MAX_OFFSET) {
+                return Answer.YES;
+            }
+            if (relativeDistanceBefore < MAX_OFFSET && relativeDistanceAfter < MAX_OFFSET) {
+                if (relativeDistanceBefore == 0 || relativeDistanceAfter == 0) {
+                    return Answer.MAYBE; // unlucky case where
+                }
+                return Answer.NO;
+            }
+            return Answer.MAYBE;
+        }
+
+        static void testWithOffset(String name, long offset)
+                throws IllegalAccessException {
+            testWithOffset(name, offset, systemUTC);
+        }
+
+        static void testWithOffset(String name, long offset, Clock clock)
+                throws IllegalAccessException {
+            offsetField.set(clock, offset);
+            long beforeMillis = System.currentTimeMillis();
+            final Instant instant = clock.instant();
+            long afterMillis = System.currentTimeMillis();
+            long actualOffset = offsetField.getLong(clock);
+            long instantMillis = instant.getEpochSecond() * MILLIS_IN_SECOND
+                    + instant.getNano() / NANOS_IN_MILLI;
+            if (instantMillis < beforeMillis || instantMillis > afterMillis) {
+                throw new RuntimeException(name
+                        + ": Invalid instant: " + instant
+                        + " (~" + instantMillis + "ms)"
+                        + " when time in millis is in ["
+                        + beforeMillis + ", " + afterMillis
+                        + "] and offset in seconds is " + offset);
+            }
+            Answer isOffLimits = isOffLimits(beforeMillis / MILLIS_IN_SECOND,
+                    afterMillis / MILLIS_IN_SECOND, offset);
+            switch (isOffLimits) {
+                case YES:
+                    if (actualOffset == offset) {
+                        throw new RuntimeException(name
+                                + ": offset was offlimit but was not recomputed "
+                                + " when time in millis is in ["
+                                + beforeMillis + ", " + afterMillis
+                                + "] and offset in seconds was " + offset);
+                    }
+                    break;
+                case NO:
+                    if (actualOffset != offset) {
+                        throw new RuntimeException(name
+                                + ": offset was not offlimit but was recomputed.");
+                    }
+                    break;
+                default:
+                    break;
+            }
+            if (distance(actualOffset, instant.getEpochSecond()) >= MAX_OFFSET) {
+                throw new RuntimeException(name + ": Actual offset is too far off:"
+                        + " offset=" + actualOffset
+                        + "instant.seconds=" + instant.getEpochSecond());
+            }
+            long adjustment = (instant.getEpochSecond() - actualOffset) * NANOS_IN_SECOND
+                    + instant.getNano();
+            validateAdjustment(name, actualOffset, beforeMillis, afterMillis, adjustment);
+        }
+
+        static void validateAdjustment(String name, long offset, long beforeMillis,
+                long afterMillis, long adjustment) {
+            System.out.println("Validating adjustment: " + adjustment);
+            long expectedMax = distance(offset, beforeMillis / MILLIS_IN_SECOND)
+                    * NANOS_IN_SECOND
+                    + (beforeMillis % MILLIS_IN_SECOND) * NANOS_IN_MILLI
+                    + (afterMillis - beforeMillis + 1) * NANOS_IN_MILLI;
+            long absoluteAdjustment = distance(0, adjustment);
+            if (absoluteAdjustment > expectedMax) {
+                long adjSec = absoluteAdjustment / NANOS_IN_SECOND;
+                long adjMil = (absoluteAdjustment % NANOS_IN_SECOND) / NANOS_IN_MILLI;
+                long adjMic = (absoluteAdjustment % NANOS_IN_MILLI) / NANOS_IN_MICRO;
+                long adjNan = (absoluteAdjustment % NANOS_IN_MICRO);
+                long expSec = expectedMax / NANOS_IN_SECOND;
+                long expMil = (expectedMax % NANOS_IN_SECOND) / NANOS_IN_MILLI;
+                long expMic = (expectedMax % NANOS_IN_MILLI) / NANOS_IN_MICRO;
+                long expNan = (expectedMax % NANOS_IN_MICRO);
+                System.err.println("Excessive adjustment: " + adjSec + "s, "
+                        + adjMil + "ms, " + adjMic + "mics, " + adjNan + "ns");
+                System.err.println("Epected max: " + expSec + "s, "
+                        + expMil + "ms, " + expMic + "mics, " + expNan + "ns");
+
+                throw new RuntimeException(name
+                        + ": Excessive adjustment: " + adjustment
+                        + " when time in millis is in ["
+                        + beforeMillis + ", " + afterMillis
+                        + "] and offset in seconds is " + offset);
+            }
+        }
+    }
+
+    public void test_OffsetRegular() throws IllegalAccessException {
+        System.out.println("*** Testing regular cases ***");
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000",
+                System.currentTimeMillis()/1000);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - 1024",
+                System.currentTimeMillis()/1000 - 1024);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + 1024",
+                System.currentTimeMillis()/1000 + 1024);
+    }
+
+    public void test_OffsetLimits() throws IllegalAccessException {
+        System.out.println("*** Testing limits ***");
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET + 1",
+                System.currentTimeMillis()/1000 - MAX_OFFSET + 1);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET - 1",
+                System.currentTimeMillis()/1000 + MAX_OFFSET - 1);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET",
+                System.currentTimeMillis()/1000 - MAX_OFFSET);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET",
+                System.currentTimeMillis()/1000 + MAX_OFFSET);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET - 1024",
+                System.currentTimeMillis()/1000 - MAX_OFFSET - 1024);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET + 1024",
+                System.currentTimeMillis()/1000 + MAX_OFFSET + 1024);
+        SystemClockOffset.testWithOffset("0", 0);
+        SystemClockOffset.testWithOffset("-1", -1);
+        SystemClockOffset.testWithOffset("Integer.MAX_VALUE + System.currentTimeMillis()/1000",
+                ((long)Integer.MAX_VALUE) + System.currentTimeMillis()/1000);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Integer.MIN_VALUE",
+                System.currentTimeMillis()/1000 - Integer.MIN_VALUE);
+        SystemClockOffset.testWithOffset("Long.MAX_VALUE", Long.MAX_VALUE);
+        SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Long.MIN_VALUE",
+                (Long.MIN_VALUE + System.currentTimeMillis()/1000)*-1);
+    }
 }
< prev index next >