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