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