• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2012, 2016, 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.Test;
72 import org.testng.annotations.DataProvider;
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 count=0;
181             // let's preheat the system a bit:
182             int lastNanos = 0;
183             for (int i = 0; i < 1000 ; i++) {
184                 system1 = Instant.ofEpochMilli(System.currentTimeMillis());
185                 final int sysnan = system1.getNano();
186                 int nanos;
187                 do {
188                     highest1 = highestUTC.instant();
189                     nanos = highest1.getNano();
190                 } while (nanos == lastNanos); // Repeat to get a different value
191                 lastNanos = nanos;
192 
193                 if ((nanos % 1000000) > 0) {
194                     count++; // we have micro seconds
195                 }
196                 if ((sysnan % 1000000) > 0) {
197                     throw new RuntimeException("Expected only millisecconds "
198                             + "precision for systemUTC, found "
199                             + (sysnan % 1000000) + " remainder.");
200                 }
201             }
202             System.out.println("\nNumber of time stamps which had better than"
203                     + " millisecond precision: "+count+"/"+1000);
204             System.out.println(formatTime("\nsystemUTC            ", system1));
205             System.out.println(formatTime("highestResolutionUTC ", highest1));
206             if (count == 0) {
207                 System.err.println("Something is strange: no microsecond "
208                         + "precision with highestResolutionUTC?");
209                 throw new RuntimeException("Micro second preccision not reached");
210             }
211 
212             // check again
213             if (highest1.isBefore(system1)) {
214                 System.err.println("highest1 is before system1!");
215                 System.err.println(formatTime("\n\tsystem1", system1));
216                 System.err.println(formatTime("\n\thighest1", highest1));
217                 throw new RuntimeException("highest1 is before system1!"
218                         + formatTime("\n\tsystem1", system1)
219                         + formatTime("\n\thighest1", highest1));
220             }
221 
222             // leap of faith: ensure that highest1 is from within 10 secs of
223             //   system1
224             if (highest1.toEpochMilli() != system1.toEpochMilli()) {
225                 long delta = highest1.getEpochSecond() - system1.getEpochSecond();
226                 if (delta > 10) {
227                     throw new RuntimeException("Unexpected long delay between two clocks ("
228                             + delta + " seconds)"
229                             + formatTime("\n\t system1", system1)
230                             + formatTime("\n\t highest1", highest1));
231 
232                 }
233             } else {
234                 System.out.println("You won the lottery: the two dates are within 1 millisecond!\n");
235             }
236 
237         } finally {
238             Instant stop = Instant.ofEpochMilli(System.currentTimeMillis());
239             if (start.isAfter(stop)) {
240                 // This should not happen - but can (un)probably be observed
241                 // when switching to summer time, or if another application
242                 // is switching the system date...
243                 System.err.println("Cannot test - date was setback: "
244                         + formatTime("\n\tstarted at", start)
245                         + formatTime("\n\tstopped at", stop) + "\n");
246                 return; // will prevent exceptions from being propagated.
247             }
248         }
249     }
250 
251     static final long MAX_OFFSET = 0x0100000000L;
252     static final long MIN_OFFSET = -MAX_OFFSET;
253 
254     // A helper class to test that SystemClock correctly recomputes
255     // its offset.
256     static class SystemClockOffset {
257 
258         static final int MILLIS_IN_SECOND = 1000;
259         static final int NANOS_IN_MILLI = 1000_000;
260         static final int NANOS_IN_MICRO = 1000;
261         static final int NANOS_IN_SECOND = 1000_000_000;
262 
263         static final boolean verbose = true;
264         static final Clock systemUTC = Clock.systemUTC();
265         static final Field offsetField;
266 
267         static {
268             try {
269                 offsetField = Class.forName("java.time.Clock$SystemClock").getDeclaredField("offset");
270                 offsetField.setAccessible(true);
271             } catch (ClassNotFoundException | NoSuchFieldException ex) {
272                 throw new ExceptionInInitializerError(ex);
273             }
274         }
275 
276         static enum Answer {
277 
278             YES, // isOffLimit = YES:   we must get -1
279             NO, // isOffLimit = NO:    we must not not get -1
280             MAYBE  // isOffLimit = MAYBE: we might get -1 or a valid adjustment.
281         };
282 
distance(long one, long two)283         static long distance(long one, long two) {
284             return one > two ? Math.subtractExact(one, two)
285                     : Math.subtractExact(two, one);
286         }
287 
isOffLimits(long before, long after, long offset)288         static Answer isOffLimits(long before, long after, long offset) {
289             long relativeDistanceBefore = distance(before, offset);
290             long relativeDistanceAfter = distance(after, offset);
291             if (relativeDistanceBefore >= MAX_OFFSET && relativeDistanceAfter >= MAX_OFFSET) {
292                 return Answer.YES;
293             }
294             if (relativeDistanceBefore < MAX_OFFSET && relativeDistanceAfter < MAX_OFFSET) {
295                 if (relativeDistanceBefore == 0 || relativeDistanceAfter == 0) {
296                     return Answer.MAYBE; // unlucky case where
297                 }
298                 return Answer.NO;
299             }
300             return Answer.MAYBE;
301         }
302 
testWithOffset(String name, long offset)303         static void testWithOffset(String name, long offset)
304                 throws IllegalAccessException {
305             testWithOffset(name, offset, systemUTC);
306         }
307 
testWithOffset(String name, long offset, Clock clock)308         static void testWithOffset(String name, long offset, Clock clock)
309                 throws IllegalAccessException {
310             offsetField.set(clock, offset);
311             long beforeMillis = System.currentTimeMillis();
312             final Instant instant = clock.instant();
313             long afterMillis = System.currentTimeMillis();
314             long actualOffset = offsetField.getLong(clock);
315             long instantMillis = instant.getEpochSecond() * MILLIS_IN_SECOND
316                     + instant.getNano() / NANOS_IN_MILLI;
317             if (instantMillis < beforeMillis || instantMillis > afterMillis) {
318                 throw new RuntimeException(name
319                         + ": Invalid instant: " + instant
320                         + " (~" + instantMillis + "ms)"
321                         + " when time in millis is in ["
322                         + beforeMillis + ", " + afterMillis
323                         + "] and offset in seconds is " + offset);
324             }
325             Answer isOffLimits = isOffLimits(beforeMillis / MILLIS_IN_SECOND,
326                     afterMillis / MILLIS_IN_SECOND, offset);
327             switch (isOffLimits) {
328                 case YES:
329                     if (actualOffset == offset) {
330                         throw new RuntimeException(name
331                                 + ": offset was offlimit but was not recomputed "
332                                 + " when time in millis is in ["
333                                 + beforeMillis + ", " + afterMillis
334                                 + "] and offset in seconds was " + offset);
335                     }
336                     break;
337                 case NO:
338                     if (actualOffset != offset) {
339                         throw new RuntimeException(name
340                                 + ": offset was not offlimit but was recomputed.");
341                     }
342                     break;
343                 default:
344                     break;
345             }
346             if (distance(actualOffset, instant.getEpochSecond()) >= MAX_OFFSET) {
347                 throw new RuntimeException(name + ": Actual offset is too far off:"
348                         + " offset=" + actualOffset
349                         + "instant.seconds=" + instant.getEpochSecond());
350             }
351             long adjustment = (instant.getEpochSecond() - actualOffset) * NANOS_IN_SECOND
352                     + instant.getNano();
353             validateAdjustment(name, actualOffset, beforeMillis, afterMillis, adjustment);
354         }
355 
validateAdjustment(String name, long offset, long beforeMillis, long afterMillis, long adjustment)356         static void validateAdjustment(String name, long offset, long beforeMillis,
357                 long afterMillis, long adjustment) {
358             System.out.println("Validating adjustment: " + adjustment);
359             long expectedMax = distance(offset, beforeMillis / MILLIS_IN_SECOND)
360                     * NANOS_IN_SECOND
361                     + (beforeMillis % MILLIS_IN_SECOND) * NANOS_IN_MILLI
362                     + (afterMillis - beforeMillis + 1) * NANOS_IN_MILLI;
363             long absoluteAdjustment = distance(0, adjustment);
364             if (absoluteAdjustment > expectedMax) {
365                 long adjSec = absoluteAdjustment / NANOS_IN_SECOND;
366                 long adjMil = (absoluteAdjustment % NANOS_IN_SECOND) / NANOS_IN_MILLI;
367                 long adjMic = (absoluteAdjustment % NANOS_IN_MILLI) / NANOS_IN_MICRO;
368                 long adjNan = (absoluteAdjustment % NANOS_IN_MICRO);
369                 long expSec = expectedMax / NANOS_IN_SECOND;
370                 long expMil = (expectedMax % NANOS_IN_SECOND) / NANOS_IN_MILLI;
371                 long expMic = (expectedMax % NANOS_IN_MILLI) / NANOS_IN_MICRO;
372                 long expNan = (expectedMax % NANOS_IN_MICRO);
373                 System.err.println("Excessive adjustment: " + adjSec + "s, "
374                         + adjMil + "ms, " + adjMic + "mics, " + adjNan + "ns");
375                 System.err.println("Epected max: " + expSec + "s, "
376                         + expMil + "ms, " + expMic + "mics, " + expNan + "ns");
377 
378                 throw new RuntimeException(name
379                         + ": Excessive adjustment: " + adjustment
380                         + " when time in millis is in ["
381                         + beforeMillis + ", " + afterMillis
382                         + "] and offset in seconds is " + offset);
383             }
384         }
385     }
386 
test_OffsetRegular()387     public void test_OffsetRegular() throws IllegalAccessException {
388         System.out.println("*** Testing regular cases ***");
389         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000",
390                 System.currentTimeMillis()/1000);
391         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - 1024",
392                 System.currentTimeMillis()/1000 - 1024);
393         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + 1024",
394                 System.currentTimeMillis()/1000 + 1024);
395     }
396 
test_OffsetLimits()397     public void test_OffsetLimits() throws IllegalAccessException {
398         System.out.println("*** Testing limits ***");
399         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET + 1",
400                 System.currentTimeMillis()/1000 - MAX_OFFSET + 1);
401         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET - 1",
402                 System.currentTimeMillis()/1000 + MAX_OFFSET - 1);
403         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET",
404                 System.currentTimeMillis()/1000 - MAX_OFFSET);
405         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET",
406                 System.currentTimeMillis()/1000 + MAX_OFFSET);
407         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET - 1024",
408                 System.currentTimeMillis()/1000 - MAX_OFFSET - 1024);
409         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET + 1024",
410                 System.currentTimeMillis()/1000 + MAX_OFFSET + 1024);
411         SystemClockOffset.testWithOffset("0", 0);
412         SystemClockOffset.testWithOffset("-1", -1);
413         SystemClockOffset.testWithOffset("Integer.MAX_VALUE + System.currentTimeMillis()/1000",
414                 ((long)Integer.MAX_VALUE) + System.currentTimeMillis()/1000);
415         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Integer.MIN_VALUE",
416                 System.currentTimeMillis()/1000 - Integer.MIN_VALUE);
417         SystemClockOffset.testWithOffset("Long.MAX_VALUE", Long.MAX_VALUE);
418         SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Long.MIN_VALUE",
419                 (Long.MIN_VALUE + System.currentTimeMillis()/1000)*-1);
420     }
421 }
422