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