• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * This file is available under and governed by the GNU General Public
26  * License version 2 only, as published by the Free Software Foundation.
27  * However, the following notice accompanied the original version of this
28  * file:
29  *
30  * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos
31  *
32  * All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions are met:
36  *
37  *  * Redistributions of source code must retain the above copyright notice,
38  *    this list of conditions and the following disclaimer.
39  *
40  *  * Redistributions in binary form must reproduce the above copyright notice,
41  *    this list of conditions and the following disclaimer in the documentation
42  *    and/or other materials provided with the distribution.
43  *
44  *  * Neither the name of JSR-310 nor the names of its contributors
45  *    may be used to endorse or promote products derived from this software
46  *    without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
52  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
53  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
54  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
55  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
56  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
57  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59  */
60 package test.java.time;
61 
62 import static org.testng.Assert.assertEquals;
63 import static org.testng.Assert.assertSame;
64 
65 import java.lang.reflect.Field;
66 import java.time.Clock;
67 import java.time.Instant;
68 import java.time.ZoneId;
69 import java.time.ZoneOffset;
70 
71 import org.testng.annotations.DataProvider;
72 import org.testng.annotations.Test;
73 
74 /**
75  * Test system clock.
76  */
77 @Test
78 public class TestClock_System {
79 
80     private static final ZoneId PARIS = ZoneId.of("Europe/Paris");
81     private static final Clock systemUTC = Clock.systemUTC();
82 
test_withZone_same()83     public void test_withZone_same() {
84         Clock test = Clock.system(PARIS);
85         Clock changed = test.withZone(PARIS);
86         assertSame(test, changed);
87     }
88 
89     //-----------------------------------------------------------------------
test_toString()90     public void test_toString() {
91         Clock test = Clock.system(PARIS);
92         assertEquals(test.toString(), "SystemClock[Europe/Paris]");
93     }
94 
95     //-----------------------------------------------------------------------
96     @DataProvider(name="sampleSystemUTC")
provider_sampleSystemUTC()97     Object[][] provider_sampleSystemUTC() {
98         return new Object[][] {
99             {"Clock.systemUTC()#1",  Clock.systemUTC()},
100             {"Clock.systemUTC()#2",  Clock.systemUTC()},
101             {"Clock.system(ZoneOffset.UTC)#1",  Clock.system(ZoneOffset.UTC)},
102             {"Clock.system(ZoneOffset.UTC)#2",  Clock.system(ZoneOffset.UTC)}
103         };
104     }
105 
106     // Test for 8073394
107     @Test(dataProvider="sampleSystemUTC")
test_systemUTC(String s, Clock clock)108     public void test_systemUTC(String s, Clock clock) {
109         if (clock != systemUTC) {
110             throw new RuntimeException("Unexpected clock instance for " + s + ": "
111                 + "\n\texpected: " + toString(systemUTC)
112                 + "\n\tactual:   " + toString(clock));
113         }
114     }
115 
toString(Clock c)116     private static String toString(Clock c) {
117         return c == null ? null :
118                c + " " + c.getClass().getName() + "@" + System.identityHashCode(c);
119     }
120 
121     //-----------------------------------------------------------------------
122 
formatTime(String prefix, Instant time)123     private static String formatTime(String prefix, Instant time) {
124         return prefix + ": " + time + " - seconds: "
125                         + time.getEpochSecond() + ", nanos: "
126                         + time.getNano();
127     }
128 
test_ClockResolution()129     public void test_ClockResolution() {
130         Clock highestUTC = Clock.systemUTC();
131 
132         Instant start = Instant.ofEpochMilli(System.currentTimeMillis());
133 
134         try {
135             // smoke test
136             Instant system1 = Instant.ofEpochMilli(System.currentTimeMillis());
137             Instant system2 = Instant.ofEpochMilli(System.currentTimeMillis());
138             Instant highest1 = highestUTC.instant();
139             Instant highest2 = highestUTC.instant();
140             System.out.println(formatTime("\nsystemUTC #1            ", system1));
141             System.out.println(formatTime("systemUTC #2            ", system2));
142             System.out.println(formatTime("highestResolutionUTC #1 ", highest1));
143             System.out.println(formatTime("highestResolutionUTC #2 ", highest2));
144 
145             if (system2.isBefore(system1)) {
146                 System.err.println("system2 is before system1!");
147                 System.err.println(formatTime("\n\tsystem1", system1));
148                 System.err.println(formatTime("\n\tsystem2", system2));
149                 throw new RuntimeException("system2 is before system1!"
150                         + formatTime("\n\tsystem1", system1)
151                         + formatTime("\n\tsystem2", system2));
152             }
153             if (highest2.isBefore(highest1)) {
154                 System.err.println("highest2 is before highest1!");
155                 System.err.println(formatTime("\n\thighest1", system1));
156                 System.err.println(formatTime("\n\tsystem2", highest2));
157                 throw new RuntimeException("highest2 is before system1!"
158                         + formatTime("\n\thighest1", system1)
159                         + formatTime("\n\tsystem2", highest2));
160             }
161 
162             // better test - but depends on implementation details.
163             // we're not rounding - so highest1 should be greater or equal to
164             // system1
165             system1 = Instant.ofEpochMilli(System.currentTimeMillis());
166             highest1 = highestUTC.instant();
167 
168             System.out.println(formatTime("\nsystemUTC            ", system1));
169             System.out.println(formatTime("highestResolutionUTC ", highest1));
170 
171             if (highest1.isBefore(system1)) {
172                 System.err.println("highest1 is before system1!");
173                 System.err.println(formatTime("\n\tsystem1", system1));
174                 System.err.println(formatTime("\n\thighest1", highest1));
175                 throw new RuntimeException("highest1 is before system1!"
176                         + formatTime("\n\tsystem1", system1)
177                         + formatTime("\n\thighest1", highest1));
178             }
179 
180             int countBetterThanMillisPrecision = 0;
181             int countBetterThanMicrosPrecision = 0;
182             // let's preheat the system a bit:
183             int lastNanos = 0;
184             for (int i = 0; i < 1000 ; i++) {
185                 system1 = Instant.ofEpochMilli(System.currentTimeMillis());
186                 final int sysnan = system1.getNano();
187                 int nanos;
188                 do {
189                     highest1 = highestUTC.instant();
190                     nanos = highest1.getNano();
191                 } while (nanos == lastNanos); // Repeat to get a different value
192                 lastNanos = nanos;
193 
194                 if ((nanos % 1000000) > 0) {
195                     countBetterThanMillisPrecision++; // we have microseconds
196                 }
197                 if ((nanos % 1000) > 0) {
198                     countBetterThanMicrosPrecision++; // we have nanoseconds
199                 }
200                 if ((sysnan % 1000000) > 0) {
201                     throw new RuntimeException("Expected only millisecconds "
202                             + "precision for systemUTC, found "
203                             + (sysnan % 1000000) + " remainder.");
204                 }
205             }
206             System.out.println("\nNumber of time stamps which had better than"
207                                + " millisecond precision: "
208                                + countBetterThanMillisPrecision + "/" + 1000);
209             System.out.println("\nNumber of time stamps which had better than"
210                                + " microsecond precision: "
211                                + countBetterThanMicrosPrecision + "/" + 1000);
212             System.out.println(formatTime("\nsystemUTC            ", system1));
213             System.out.println(formatTime("highestResolutionUTC ", highest1));
214             if (countBetterThanMillisPrecision == 0) {
215                 System.err.println("Something is strange: no microsecond "
216                                    + "precision with highestResolutionUTC?");
217                 throw new RuntimeException("Micro second precision not reached");
218             }
219 
220             // check again
221             if (highest1.isBefore(system1)) {
222                 System.err.println("highest1 is before system1!");
223                 System.err.println(formatTime("\n\tsystem1", system1));
224                 System.err.println(formatTime("\n\thighest1", highest1));
225                 throw new RuntimeException("highest1 is before system1!"
226                         + formatTime("\n\tsystem1", system1)
227                         + formatTime("\n\thighest1", highest1));
228             }
229 
230             // leap of faith: ensure that highest1 is from within 10 secs of
231             //   system1
232             if (highest1.toEpochMilli() != system1.toEpochMilli()) {
233                 long delta = highest1.getEpochSecond() - system1.getEpochSecond();
234                 if (delta > 10) {
235                     throw new RuntimeException("Unexpected long delay between two clocks ("
236                             + delta + " seconds)"
237                             + formatTime("\n\t system1", system1)
238                             + formatTime("\n\t highest1", highest1));
239 
240                 }
241             } else {
242                 System.out.println("You won the lottery: the two dates are within 1 millisecond!\n");
243             }
244 
245         } finally {
246             Instant stop = Instant.ofEpochMilli(System.currentTimeMillis());
247             if (start.isAfter(stop)) {
248                 // This should not happen - but can (un)probably be observed
249                 // when switching to summer time, or if another application
250                 // is switching the system date...
251                 System.err.println("Cannot test - date was setback: "
252                         + formatTime("\n\tstarted at", start)
253                         + formatTime("\n\tstopped at", stop) + "\n");
254                 return; // will prevent exceptions from being propagated.
255             }
256         }
257     }
258 
259     static final long MAX_OFFSET = 0x0100000000L;
260     static final long MIN_OFFSET = -MAX_OFFSET;
261 
262     // A helper class to test that SystemClock correctly recomputes
263     // its offset.
264     static class SystemClockOffset {
265 
266         static final int MILLIS_IN_SECOND = 1000;
267         static final int NANOS_IN_MILLI = 1000_000;
268         static final int NANOS_IN_MICRO = 1000;
269         static final int NANOS_IN_SECOND = 1000_000_000;
270 
271         static final boolean verbose = true;
272         static final Clock systemUTC = Clock.systemUTC();
273         static final Field offsetField;
274 
275         static {
276             try {
277                 offsetField = Class.forName("java.time.Clock").getDeclaredField("offset");
278                 offsetField.setAccessible(true);
279             } catch (ClassNotFoundException | NoSuchFieldException ex) {
280                 throw new ExceptionInInitializerError(ex);
281             }
282         }
283 
284         static enum Answer {
285 
286             YES, // isOffLimit = YES:   we must get -1
287             NO, // isOffLimit = NO:    we must not not get -1
288             MAYBE  // isOffLimit = MAYBE: we might get -1 or a valid adjustment.
289         };
290 
distance(long one, long two)291         static long distance(long one, long two) {
292             return one > two ? Math.subtractExact(one, two)
293                     : Math.subtractExact(two, one);
294         }
295 
isOffLimits(long before, long after, long offset)296         static Answer isOffLimits(long before, long after, long offset) {
297             long relativeDistanceBefore = distance(before, offset);
298             long relativeDistanceAfter = distance(after, offset);
299             if (relativeDistanceBefore >= MAX_OFFSET && relativeDistanceAfter >= MAX_OFFSET) {
300                 return Answer.YES;
301             }
302             if (relativeDistanceBefore < MAX_OFFSET && relativeDistanceAfter < MAX_OFFSET) {
303                 if (relativeDistanceBefore == 0 || relativeDistanceAfter == 0) {
304                     return Answer.MAYBE; // unlucky case where
305                 }
306                 return Answer.NO;
307             }
308             return Answer.MAYBE;
309         }
310 
testWithOffset(String name, long offset)311         static void testWithOffset(String name, long offset)
312                 throws IllegalAccessException {
313             testWithOffset(name, offset, systemUTC);
314         }
315 
testWithOffset(String name, long offset, Clock clock)316         static void testWithOffset(String name, long offset, Clock clock)
317                 throws IllegalAccessException {
318             offsetField.set(null, offset);
319             long beforeMillis = System.currentTimeMillis();
320             final Instant instant = clock.instant();
321             long afterMillis = System.currentTimeMillis();
322             long actualOffset = offsetField.getLong(null);
323             long instantMillis = instant.getEpochSecond() * MILLIS_IN_SECOND
324                     + instant.getNano() / NANOS_IN_MILLI;
325             if (instantMillis < beforeMillis || instantMillis > afterMillis) {
326                 throw new RuntimeException(name
327                         + ": Invalid instant: " + instant
328                         + " (~" + instantMillis + "ms)"
329                         + " when time in millis is in ["
330                         + beforeMillis + ", " + afterMillis
331                         + "] and offset in seconds is " + offset);
332             }
333             Answer isOffLimits = isOffLimits(beforeMillis / MILLIS_IN_SECOND,
334                     afterMillis / MILLIS_IN_SECOND, offset);
335             switch (isOffLimits) {
336                 case YES:
337                     if (actualOffset == offset) {
338                         throw new RuntimeException(name
339                                 + ": offset was offlimit but was not recomputed "
340                                 + " when time in millis is in ["
341                                 + beforeMillis + ", " + afterMillis
342                                 + "] and offset in seconds was " + offset);
343                     }
344                     break;
345                 case NO:
346                     if (actualOffset != offset) {
347                         throw new RuntimeException(name
348                                 + ": offset was not offlimit but was recomputed.");
349                     }
350                     break;
351                 default:
352                     break;
353             }
354             if (distance(actualOffset, instant.getEpochSecond()) >= MAX_OFFSET) {
355                 throw new RuntimeException(name + ": Actual offset is too far off:"
356                         + " offset=" + actualOffset
357                         + "instant.seconds=" + instant.getEpochSecond());
358             }
359             long adjustment = (instant.getEpochSecond() - actualOffset) * NANOS_IN_SECOND
360                     + instant.getNano();
361             validateAdjustment(name, actualOffset, beforeMillis, afterMillis, adjustment);
362         }
363 
validateAdjustment(String name, long offset, long beforeMillis, long afterMillis, long adjustment)364         static void validateAdjustment(String name, long offset, long beforeMillis,
365                 long afterMillis, long adjustment) {
366             System.out.println("Validating adjustment: " + adjustment);
367             long expectedMax = distance(offset, beforeMillis / MILLIS_IN_SECOND)
368                     * NANOS_IN_SECOND
369                     + (beforeMillis % MILLIS_IN_SECOND) * NANOS_IN_MILLI
370                     + (afterMillis - beforeMillis + 1) * NANOS_IN_MILLI;
371             long absoluteAdjustment = distance(0, adjustment);
372             if (absoluteAdjustment > expectedMax) {
373                 long adjSec = absoluteAdjustment / NANOS_IN_SECOND;
374                 long adjMil = (absoluteAdjustment % NANOS_IN_SECOND) / NANOS_IN_MILLI;
375                 long adjMic = (absoluteAdjustment % NANOS_IN_MILLI) / NANOS_IN_MICRO;
376                 long adjNan = (absoluteAdjustment % NANOS_IN_MICRO);
377                 long expSec = expectedMax / NANOS_IN_SECOND;
378                 long expMil = (expectedMax % NANOS_IN_SECOND) / NANOS_IN_MILLI;
379                 long expMic = (expectedMax % NANOS_IN_MILLI) / NANOS_IN_MICRO;
380                 long expNan = (expectedMax % NANOS_IN_MICRO);
381                 System.err.println("Excessive adjustment: " + adjSec + "s, "
382                         + adjMil + "ms, " + adjMic + "mics, " + adjNan + "ns");
383                 System.err.println("Epected max: " + expSec + "s, "
384                         + expMil + "ms, " + expMic + "mics, " + expNan + "ns");
385 
386                 throw new RuntimeException(name
387                         + ": Excessive adjustment: " + adjustment
388                         + " when time in millis is in ["
389                         + beforeMillis + ", " + afterMillis
390                         + "] and offset in seconds is " + offset);
391             }
392         }
393     }
394 
test_OffsetRegular()395     public void test_OffsetRegular() throws IllegalAccessException {
396         System.out.println("*** Testing regular cases ***");
397         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000",
398                 System.currentTimeMillis()/1000);
399         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - 1024",
400                 System.currentTimeMillis()/1000 - 1024);
401         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + 1024",
402                 System.currentTimeMillis()/1000 + 1024);
403     }
404 
test_OffsetLimits()405     public void test_OffsetLimits() throws IllegalAccessException {
406         System.out.println("*** Testing limits ***");
407         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET + 1",
408                 System.currentTimeMillis()/1000 - MAX_OFFSET + 1);
409         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET - 1",
410                 System.currentTimeMillis()/1000 + MAX_OFFSET - 1);
411         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET",
412                 System.currentTimeMillis()/1000 - MAX_OFFSET);
413         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET",
414                 System.currentTimeMillis()/1000 + MAX_OFFSET);
415         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET - 1024",
416                 System.currentTimeMillis()/1000 - MAX_OFFSET - 1024);
417         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET + 1024",
418                 System.currentTimeMillis()/1000 + MAX_OFFSET + 1024);
419         SystemClockOffset.testWithOffset("0", 0);
420         SystemClockOffset.testWithOffset("-1", -1);
421         SystemClockOffset.testWithOffset("Integer.MAX_VALUE + System.currentTimeMillis()/1000",
422                 ((long)Integer.MAX_VALUE) + System.currentTimeMillis()/1000);
423         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Integer.MIN_VALUE",
424                 System.currentTimeMillis()/1000 - Integer.MIN_VALUE);
425         SystemClockOffset.testWithOffset("Long.MAX_VALUE", Long.MAX_VALUE);
426         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Long.MIN_VALUE",
427                 (Long.MIN_VALUE + System.currentTimeMillis()/1000)*-1);
428     }
429 }
430