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 libcore.test.annotation.NonCts; 66 import libcore.test.reasons.NonCtsReasons; 67 68 import java.lang.reflect.Field; 69 import java.time.Clock; 70 import java.time.Instant; 71 import java.time.ZoneId; 72 import java.time.ZoneOffset; 73 74 import org.testng.annotations.DataProvider; 75 import org.testng.annotations.Test; 76 77 /** 78 * Test system clock. 79 */ 80 @Test 81 public class TestClock_System { 82 83 private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); 84 private static final Clock systemUTC = Clock.systemUTC(); 85 test_withZone_same()86 public void test_withZone_same() { 87 Clock test = Clock.system(PARIS); 88 Clock changed = test.withZone(PARIS); 89 assertSame(test, changed); 90 } 91 92 //----------------------------------------------------------------------- test_toString()93 public void test_toString() { 94 Clock test = Clock.system(PARIS); 95 assertEquals(test.toString(), "SystemClock[Europe/Paris]"); 96 } 97 98 //----------------------------------------------------------------------- 99 @DataProvider(name="sampleSystemUTC") provider_sampleSystemUTC()100 Object[][] provider_sampleSystemUTC() { 101 return new Object[][] { 102 {"Clock.systemUTC()#1", Clock.systemUTC()}, 103 {"Clock.systemUTC()#2", Clock.systemUTC()}, 104 {"Clock.system(ZoneOffset.UTC)#1", Clock.system(ZoneOffset.UTC)}, 105 {"Clock.system(ZoneOffset.UTC)#2", Clock.system(ZoneOffset.UTC)} 106 }; 107 } 108 109 // Test for 8073394 110 @Test(dataProvider="sampleSystemUTC") test_systemUTC(String s, Clock clock)111 public void test_systemUTC(String s, Clock clock) { 112 if (clock != systemUTC) { 113 throw new RuntimeException("Unexpected clock instance for " + s + ": " 114 + "\n\texpected: " + toString(systemUTC) 115 + "\n\tactual: " + toString(clock)); 116 } 117 } 118 toString(Clock c)119 private static String toString(Clock c) { 120 return c == null ? null : 121 c + " " + c.getClass().getName() + "@" + System.identityHashCode(c); 122 } 123 124 //----------------------------------------------------------------------- 125 formatTime(String prefix, Instant time)126 private static String formatTime(String prefix, Instant time) { 127 return prefix + ": " + time + " - seconds: " 128 + time.getEpochSecond() + ", nanos: " 129 + time.getNano(); 130 } 131 test_ClockResolution()132 public void test_ClockResolution() { 133 Clock highestUTC = Clock.systemUTC(); 134 135 Instant start = Instant.ofEpochMilli(System.currentTimeMillis()); 136 137 try { 138 // smoke test 139 Instant system1 = Instant.ofEpochMilli(System.currentTimeMillis()); 140 Instant system2 = Instant.ofEpochMilli(System.currentTimeMillis()); 141 Instant highest1 = highestUTC.instant(); 142 Instant highest2 = highestUTC.instant(); 143 System.out.println(formatTime("\nsystemUTC #1 ", system1)); 144 System.out.println(formatTime("systemUTC #2 ", system2)); 145 System.out.println(formatTime("highestResolutionUTC #1 ", highest1)); 146 System.out.println(formatTime("highestResolutionUTC #2 ", highest2)); 147 148 if (system2.isBefore(system1)) { 149 System.err.println("system2 is before system1!"); 150 System.err.println(formatTime("\n\tsystem1", system1)); 151 System.err.println(formatTime("\n\tsystem2", system2)); 152 throw new RuntimeException("system2 is before system1!" 153 + formatTime("\n\tsystem1", system1) 154 + formatTime("\n\tsystem2", system2)); 155 } 156 if (highest2.isBefore(highest1)) { 157 System.err.println("highest2 is before highest1!"); 158 System.err.println(formatTime("\n\thighest1", system1)); 159 System.err.println(formatTime("\n\tsystem2", highest2)); 160 throw new RuntimeException("highest2 is before system1!" 161 + formatTime("\n\thighest1", system1) 162 + formatTime("\n\tsystem2", highest2)); 163 } 164 165 // better test - but depends on implementation details. 166 // we're not rounding - so highest1 should be greater or equal to 167 // system1 168 system1 = Instant.ofEpochMilli(System.currentTimeMillis()); 169 highest1 = highestUTC.instant(); 170 171 System.out.println(formatTime("\nsystemUTC ", system1)); 172 System.out.println(formatTime("highestResolutionUTC ", highest1)); 173 174 if (highest1.isBefore(system1)) { 175 System.err.println("highest1 is before system1!"); 176 System.err.println(formatTime("\n\tsystem1", system1)); 177 System.err.println(formatTime("\n\thighest1", highest1)); 178 throw new RuntimeException("highest1 is before system1!" 179 + formatTime("\n\tsystem1", system1) 180 + formatTime("\n\thighest1", highest1)); 181 } 182 183 int countBetterThanMillisPrecision = 0; 184 int countBetterThanMicrosPrecision = 0; 185 // let's preheat the system a bit: 186 int lastNanos = 0; 187 for (int i = 0; i < 1000 ; i++) { 188 system1 = Instant.ofEpochMilli(System.currentTimeMillis()); 189 final int sysnan = system1.getNano(); 190 int nanos; 191 do { 192 highest1 = highestUTC.instant(); 193 nanos = highest1.getNano(); 194 } while (nanos == lastNanos); // Repeat to get a different value 195 lastNanos = nanos; 196 197 if ((nanos % 1000000) > 0) { 198 countBetterThanMillisPrecision++; // we have microseconds 199 } 200 if ((nanos % 1000) > 0) { 201 countBetterThanMicrosPrecision++; // we have nanoseconds 202 } 203 if ((sysnan % 1000000) > 0) { 204 throw new RuntimeException("Expected only millisecconds " 205 + "precision for systemUTC, found " 206 + (sysnan % 1000000) + " remainder."); 207 } 208 } 209 System.out.println("\nNumber of time stamps which had better than" 210 + " millisecond precision: " 211 + countBetterThanMillisPrecision + "/" + 1000); 212 System.out.println("\nNumber of time stamps which had better than" 213 + " microsecond precision: " 214 + countBetterThanMicrosPrecision + "/" + 1000); 215 System.out.println(formatTime("\nsystemUTC ", system1)); 216 System.out.println(formatTime("highestResolutionUTC ", highest1)); 217 if (countBetterThanMillisPrecision == 0) { 218 System.err.println("Something is strange: no microsecond " 219 + "precision with highestResolutionUTC?"); 220 throw new RuntimeException("Micro second precision not reached"); 221 } 222 223 // check again 224 if (highest1.isBefore(system1)) { 225 System.err.println("highest1 is before system1!"); 226 System.err.println(formatTime("\n\tsystem1", system1)); 227 System.err.println(formatTime("\n\thighest1", highest1)); 228 throw new RuntimeException("highest1 is before system1!" 229 + formatTime("\n\tsystem1", system1) 230 + formatTime("\n\thighest1", highest1)); 231 } 232 233 // leap of faith: ensure that highest1 is from within 10 secs of 234 // system1 235 if (highest1.toEpochMilli() != system1.toEpochMilli()) { 236 long delta = highest1.getEpochSecond() - system1.getEpochSecond(); 237 if (delta > 10) { 238 throw new RuntimeException("Unexpected long delay between two clocks (" 239 + delta + " seconds)" 240 + formatTime("\n\t system1", system1) 241 + formatTime("\n\t highest1", highest1)); 242 243 } 244 } else { 245 System.out.println("You won the lottery: the two dates are within 1 millisecond!\n"); 246 } 247 248 } finally { 249 Instant stop = Instant.ofEpochMilli(System.currentTimeMillis()); 250 if (start.isAfter(stop)) { 251 // This should not happen - but can (un)probably be observed 252 // when switching to summer time, or if another application 253 // is switching the system date... 254 System.err.println("Cannot test - date was setback: " 255 + formatTime("\n\tstarted at", start) 256 + formatTime("\n\tstopped at", stop) + "\n"); 257 return; // will prevent exceptions from being propagated. 258 } 259 } 260 } 261 262 static final long MAX_OFFSET = 0x0100000000L; 263 static final long MIN_OFFSET = -MAX_OFFSET; 264 265 // A helper class to test that SystemClock correctly recomputes 266 // its offset. 267 static class SystemClockOffset { 268 269 static final int MILLIS_IN_SECOND = 1000; 270 static final int NANOS_IN_MILLI = 1000_000; 271 static final int NANOS_IN_MICRO = 1000; 272 static final int NANOS_IN_SECOND = 1000_000_000; 273 274 static final boolean verbose = true; 275 static final Clock systemUTC = Clock.systemUTC(); 276 static final Field offsetField; 277 278 static { 279 try { 280 offsetField = Class.forName("java.time.Clock").getDeclaredField("offset"); 281 offsetField.setAccessible(true); 282 } catch (ClassNotFoundException | NoSuchFieldException ex) { 283 throw new ExceptionInInitializerError(ex); 284 } 285 } 286 287 static enum Answer { 288 289 YES, // isOffLimit = YES: we must get -1 290 NO, // isOffLimit = NO: we must not not get -1 291 MAYBE // isOffLimit = MAYBE: we might get -1 or a valid adjustment. 292 }; 293 distance(long one, long two)294 static long distance(long one, long two) { 295 return one > two ? Math.subtractExact(one, two) 296 : Math.subtractExact(two, one); 297 } 298 isOffLimits(long before, long after, long offset)299 static Answer isOffLimits(long before, long after, long offset) { 300 long relativeDistanceBefore = distance(before, offset); 301 long relativeDistanceAfter = distance(after, offset); 302 if (relativeDistanceBefore >= MAX_OFFSET && relativeDistanceAfter >= MAX_OFFSET) { 303 return Answer.YES; 304 } 305 if (relativeDistanceBefore < MAX_OFFSET && relativeDistanceAfter < MAX_OFFSET) { 306 if (relativeDistanceBefore == 0 || relativeDistanceAfter == 0) { 307 return Answer.MAYBE; // unlucky case where 308 } 309 return Answer.NO; 310 } 311 return Answer.MAYBE; 312 } 313 testWithOffset(String name, long offset)314 static void testWithOffset(String name, long offset) 315 throws IllegalAccessException { 316 testWithOffset(name, offset, systemUTC); 317 } 318 testWithOffset(String name, long offset, Clock clock)319 static void testWithOffset(String name, long offset, Clock clock) 320 throws IllegalAccessException { 321 offsetField.set(null, offset); 322 long beforeMillis = System.currentTimeMillis(); 323 final Instant instant = clock.instant(); 324 long afterMillis = System.currentTimeMillis(); 325 long actualOffset = offsetField.getLong(null); 326 long instantMillis = instant.getEpochSecond() * MILLIS_IN_SECOND 327 + instant.getNano() / NANOS_IN_MILLI; 328 if (instantMillis < beforeMillis || instantMillis > afterMillis) { 329 throw new RuntimeException(name 330 + ": Invalid instant: " + instant 331 + " (~" + instantMillis + "ms)" 332 + " when time in millis is in [" 333 + beforeMillis + ", " + afterMillis 334 + "] and offset in seconds is " + offset); 335 } 336 Answer isOffLimits = isOffLimits(beforeMillis / MILLIS_IN_SECOND, 337 afterMillis / MILLIS_IN_SECOND, offset); 338 switch (isOffLimits) { 339 case YES: 340 if (actualOffset == offset) { 341 throw new RuntimeException(name 342 + ": offset was offlimit but was not recomputed " 343 + " when time in millis is in [" 344 + beforeMillis + ", " + afterMillis 345 + "] and offset in seconds was " + offset); 346 } 347 break; 348 case NO: 349 if (actualOffset != offset) { 350 throw new RuntimeException(name 351 + ": offset was not offlimit but was recomputed."); 352 } 353 break; 354 default: 355 break; 356 } 357 if (distance(actualOffset, instant.getEpochSecond()) >= MAX_OFFSET) { 358 throw new RuntimeException(name + ": Actual offset is too far off:" 359 + " offset=" + actualOffset 360 + "instant.seconds=" + instant.getEpochSecond()); 361 } 362 long adjustment = (instant.getEpochSecond() - actualOffset) * NANOS_IN_SECOND 363 + instant.getNano(); 364 validateAdjustment(name, actualOffset, beforeMillis, afterMillis, adjustment); 365 } 366 validateAdjustment(String name, long offset, long beforeMillis, long afterMillis, long adjustment)367 static void validateAdjustment(String name, long offset, long beforeMillis, 368 long afterMillis, long adjustment) { 369 System.out.println("Validating adjustment: " + adjustment); 370 long expectedMax = distance(offset, beforeMillis / MILLIS_IN_SECOND) 371 * NANOS_IN_SECOND 372 + (beforeMillis % MILLIS_IN_SECOND) * NANOS_IN_MILLI 373 + (afterMillis - beforeMillis + 1) * NANOS_IN_MILLI; 374 long absoluteAdjustment = distance(0, adjustment); 375 if (absoluteAdjustment > expectedMax) { 376 long adjSec = absoluteAdjustment / NANOS_IN_SECOND; 377 long adjMil = (absoluteAdjustment % NANOS_IN_SECOND) / NANOS_IN_MILLI; 378 long adjMic = (absoluteAdjustment % NANOS_IN_MILLI) / NANOS_IN_MICRO; 379 long adjNan = (absoluteAdjustment % NANOS_IN_MICRO); 380 long expSec = expectedMax / NANOS_IN_SECOND; 381 long expMil = (expectedMax % NANOS_IN_SECOND) / NANOS_IN_MILLI; 382 long expMic = (expectedMax % NANOS_IN_MILLI) / NANOS_IN_MICRO; 383 long expNan = (expectedMax % NANOS_IN_MICRO); 384 System.err.println("Excessive adjustment: " + adjSec + "s, " 385 + adjMil + "ms, " + adjMic + "mics, " + adjNan + "ns"); 386 System.err.println("Epected max: " + expSec + "s, " 387 + expMil + "ms, " + expMic + "mics, " + expNan + "ns"); 388 389 throw new RuntimeException(name 390 + ": Excessive adjustment: " + adjustment 391 + " when time in millis is in [" 392 + beforeMillis + ", " + afterMillis 393 + "] and offset in seconds is " + offset); 394 } 395 } 396 } 397 398 @NonCts(bug = 286802267, reason = NonCtsReasons.INTERNAL_APIS) test_OffsetRegular()399 public void test_OffsetRegular() throws IllegalAccessException { 400 System.out.println("*** Testing regular cases ***"); 401 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000", 402 System.currentTimeMillis()/1000); 403 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - 1024", 404 System.currentTimeMillis()/1000 - 1024); 405 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + 1024", 406 System.currentTimeMillis()/1000 + 1024); 407 } 408 409 @NonCts(bug = 286802267, reason = NonCtsReasons.INTERNAL_APIS) test_OffsetLimits()410 public void test_OffsetLimits() throws IllegalAccessException { 411 System.out.println("*** Testing limits ***"); 412 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET + 1", 413 System.currentTimeMillis()/1000 - MAX_OFFSET + 1); 414 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET - 1", 415 System.currentTimeMillis()/1000 + MAX_OFFSET - 1); 416 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET", 417 System.currentTimeMillis()/1000 - MAX_OFFSET); 418 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET", 419 System.currentTimeMillis()/1000 + MAX_OFFSET); 420 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET - 1024", 421 System.currentTimeMillis()/1000 - MAX_OFFSET - 1024); 422 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET + 1024", 423 System.currentTimeMillis()/1000 + MAX_OFFSET + 1024); 424 SystemClockOffset.testWithOffset("0", 0); 425 SystemClockOffset.testWithOffset("-1", -1); 426 SystemClockOffset.testWithOffset("Integer.MAX_VALUE + System.currentTimeMillis()/1000", 427 ((long)Integer.MAX_VALUE) + System.currentTimeMillis()/1000); 428 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Integer.MIN_VALUE", 429 System.currentTimeMillis()/1000 - Integer.MIN_VALUE); 430 SystemClockOffset.testWithOffset("Long.MAX_VALUE", Long.MAX_VALUE); 431 SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Long.MIN_VALUE", 432 (Long.MIN_VALUE + System.currentTimeMillis()/1000)*-1); 433 } 434 } 435