@@ -296,4 +296,185 @@ class InstantTest {
296
296
assertEquals(" +19999-12-31T23:59:59.000000009Z" , LocalDateTime (19999 , 12 , 31 , 23 , 59 , 59 , 9 ).toInstant(TimeZone .UTC ).toString())
297
297
}
298
298
299
+ private val largePositiveLongs = listOf (Long .MAX_VALUE , Long .MAX_VALUE - 1 , Long .MAX_VALUE - 50 )
300
+ private val largeNegativeLongs = listOf (Long .MIN_VALUE , Long .MIN_VALUE + 1 , Long .MIN_VALUE + 50 )
301
+ @OptIn(ExperimentalTime ::class )
302
+ private val largePositiveInstants = listOf (Instant .MAX , Instant .MAX - 1 .seconds, Instant .MAX - 50 .seconds)
303
+ @OptIn(ExperimentalTime ::class )
304
+ private val largeNegativeInstants = listOf (Instant .MIN , Instant .MIN + 1 .seconds, Instant .MIN + 50 .seconds)
305
+ private val smallInstants = listOf (
306
+ Instant .fromEpochMilliseconds(0 ),
307
+ Instant .fromEpochMilliseconds(1003 ),
308
+ Instant .fromEpochMilliseconds(253112 )
309
+ )
310
+
311
+ @Test
312
+ fun testParsingThrowing () {
313
+ assertFailsWith<DateTimeFormatException > { Instant .parse(" x" ) }
314
+ assertFailsWith<DateTimeFormatException > { Instant .parse(" 12020-12-31T23:59:59.000000000Z" ) }
315
+ // this string represents an Instant that is currently larger than Instant.MAX any of the implementations:
316
+ assertFailsWith<DateTimeFormatException > { Instant .parse(" +1000000001-12-31T23:59:59.000000000Z" ) }
317
+ }
318
+
319
+ @ExperimentalTime
320
+ @Test
321
+ fun testConstructorAccessorClamping () {
322
+ // toEpochMilliseconds()/fromEpochMilliseconds()
323
+ // assuming that ranges of Long (representing a number of milliseconds) and Instant are not just overlapping,
324
+ // but one is included in the other.
325
+ if (Instant .MAX .epochSeconds > Long .MAX_VALUE / 1000 ) {
326
+ /* Any number of milliseconds in Long is representable as an Instant */
327
+ for (instant in largePositiveInstants) {
328
+ assertEquals(Long .MAX_VALUE , instant.toEpochMilliseconds(), " $instant " )
329
+ }
330
+ for (instant in largeNegativeInstants) {
331
+ assertEquals(Long .MIN_VALUE , instant.toEpochMilliseconds(), " $instant " )
332
+ }
333
+ for (milliseconds in largePositiveLongs + largeNegativeLongs) {
334
+ assertEquals(milliseconds, Instant .fromEpochMilliseconds(milliseconds).toEpochMilliseconds(),
335
+ " $milliseconds " )
336
+ }
337
+ } else {
338
+ /* Any Instant is representable as a number of milliseconds in Long */
339
+ for (milliseconds in largePositiveLongs) {
340
+ assertEquals(Instant .MAX , Instant .fromEpochMilliseconds(milliseconds), " $milliseconds " )
341
+ }
342
+ for (milliseconds in largeNegativeLongs) {
343
+ assertEquals(Instant .MIN , Instant .fromEpochMilliseconds(milliseconds), " $milliseconds " )
344
+ }
345
+ for (instant in largePositiveInstants + smallInstants + largeNegativeInstants) {
346
+ assertEquals(instant.epochSeconds,
347
+ Instant .fromEpochMilliseconds(instant.toEpochMilliseconds()).epochSeconds, " $instant " )
348
+ }
349
+ }
350
+ // fromEpochSeconds
351
+ // On all platforms Long.MAX_VALUE of seconds is not a valid instant.
352
+ for (seconds in largePositiveLongs) {
353
+ assertEquals(Instant .MAX , Instant .fromEpochSeconds(seconds, 35 ))
354
+ }
355
+ for (seconds in largeNegativeLongs) {
356
+ assertEquals(Instant .MIN , Instant .fromEpochSeconds(seconds, 35 ))
357
+ }
358
+ for (instant in largePositiveInstants + smallInstants + largeNegativeInstants) {
359
+ assertEquals(instant, Instant .fromEpochSeconds(instant.epochSeconds, instant.nanosecondsOfSecond.toLong()))
360
+ }
361
+ }
362
+
363
+ @OptIn(ExperimentalTime ::class )
364
+ @Test
365
+ fun testArithmeticClamping () {
366
+ for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
367
+ assertEquals(Instant .MAX , instant + Duration .INFINITE )
368
+ }
369
+ for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
370
+ assertEquals(Instant .MIN , instant - Duration .INFINITE )
371
+ }
372
+ assertEquals(Instant .MAX , (Instant .MAX - 4 .seconds) + 5 .seconds)
373
+ assertEquals(Instant .MIN , (Instant .MIN + 10 .seconds) - 12 .seconds)
374
+ }
375
+
376
+ @OptIn(ExperimentalTime ::class )
377
+ @Test
378
+ fun testCalendarArithmeticThrowing () {
379
+ val maxValidInstant = LocalDateTime .MAX .toInstant(TimeZone .UTC )
380
+ val minValidInstant = LocalDateTime .MIN .toInstant(TimeZone .UTC )
381
+
382
+ // Instant.plus(DateTimePeriod(), TimeZone)
383
+ // Arithmetic overflow
384
+ for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
385
+ assertFailsWith<DateTimeArithmeticException >(" $instant " ) {
386
+ instant.plus(DateTimePeriod (seconds = Long .MAX_VALUE ), TimeZone .UTC )
387
+ }
388
+ assertFailsWith<DateTimeArithmeticException >(" $instant " ) {
389
+ instant.plus(DateTimePeriod (seconds = Long .MIN_VALUE ), TimeZone .UTC )
390
+ }
391
+ }
392
+ // Overflowing a LocalDateTime in input
393
+ maxValidInstant.plus(DateTimePeriod (nanoseconds = - 1 ), TimeZone .UTC )
394
+ minValidInstant.plus(DateTimePeriod (nanoseconds = 1 ), TimeZone .UTC )
395
+ assertFailsWith<DateTimeArithmeticException > {
396
+ (maxValidInstant + 1 .nanoseconds).plus(DateTimePeriod (nanoseconds = - 2 ), TimeZone .UTC )
397
+ }
398
+ assertFailsWith<DateTimeArithmeticException > {
399
+ (minValidInstant - 1 .nanoseconds).plus(DateTimePeriod (nanoseconds = 2 ), TimeZone .UTC )
400
+ }
401
+ // Overflowing a LocalDateTime in result
402
+ assertFailsWith<DateTimeArithmeticException > {
403
+ maxValidInstant.plus(DateTimePeriod (nanoseconds = 1 ), TimeZone .UTC )
404
+ }
405
+ assertFailsWith<DateTimeArithmeticException > {
406
+ minValidInstant.plus(DateTimePeriod (nanoseconds = - 1 ), TimeZone .UTC )
407
+ }
408
+ // Overflowing a LocalDateTime in intermediate computations
409
+ assertFailsWith<DateTimeArithmeticException > {
410
+ maxValidInstant.plus(DateTimePeriod (seconds = 1 , nanoseconds = - 1_000_000_001 ), TimeZone .UTC )
411
+ }
412
+ assertFailsWith<DateTimeArithmeticException > {
413
+ maxValidInstant.plus(DateTimePeriod (hours = 1 , minutes = - 61 ), TimeZone .UTC )
414
+ }
415
+ assertFailsWith<DateTimeArithmeticException > {
416
+ maxValidInstant.plus(DateTimePeriod (days = 1 , hours = - 48 ), TimeZone .UTC )
417
+ }
418
+
419
+ // Instant.plus(Long, DateTimeUnit, TimeZone)
420
+ // Arithmetic overflow
421
+ for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
422
+ assertFailsWith<DateTimeArithmeticException >(" $instant " ) {
423
+ instant.plus(Long .MAX_VALUE , DateTimeUnit .SECOND , TimeZone .UTC )
424
+ }
425
+ assertFailsWith<DateTimeArithmeticException >(" $instant " ) {
426
+ instant.plus(Long .MIN_VALUE , DateTimeUnit .SECOND , TimeZone .UTC )
427
+ }
428
+ assertFailsWith<DateTimeArithmeticException >(" $instant " ) {
429
+ instant.plus(Long .MAX_VALUE , DateTimeUnit .YEAR , TimeZone .UTC )
430
+ }
431
+ assertFailsWith<DateTimeArithmeticException >(" $instant " ) {
432
+ instant.plus(Long .MIN_VALUE , DateTimeUnit .YEAR , TimeZone .UTC )
433
+ }
434
+ }
435
+ // Overflowing a LocalDateTime in input
436
+ maxValidInstant.plus(- 1 , DateTimeUnit .NANOSECOND , TimeZone .UTC )
437
+ minValidInstant.plus(1 , DateTimeUnit .NANOSECOND , TimeZone .UTC )
438
+ assertFailsWith<DateTimeArithmeticException > {
439
+ (maxValidInstant + 1 .nanoseconds).plus(- 2 , DateTimeUnit .NANOSECOND , TimeZone .UTC )
440
+ }
441
+ assertFailsWith<DateTimeArithmeticException > {
442
+ (minValidInstant - 1 .nanoseconds).plus(2 , DateTimeUnit .NANOSECOND , TimeZone .UTC )
443
+ }
444
+ // Overflowing a LocalDateTime in result
445
+ assertFailsWith<DateTimeArithmeticException > {
446
+ maxValidInstant.plus(1 , DateTimeUnit .NANOSECOND , TimeZone .UTC )
447
+ }
448
+ assertFailsWith<DateTimeArithmeticException > {
449
+ maxValidInstant.plus(1 , DateTimeUnit .YEAR , TimeZone .UTC )
450
+ }
451
+ assertFailsWith<DateTimeArithmeticException > {
452
+ minValidInstant.plus(- 1 , DateTimeUnit .NANOSECOND , TimeZone .UTC )
453
+ }
454
+ assertFailsWith<DateTimeArithmeticException > {
455
+ minValidInstant.plus(- 1 , DateTimeUnit .YEAR , TimeZone .UTC )
456
+ }
457
+
458
+ // Instant.periodUntil
459
+ maxValidInstant.periodUntil(minValidInstant, TimeZone .UTC )
460
+ assertFailsWith<DateTimeArithmeticException > {
461
+ (maxValidInstant + 1 .nanoseconds).periodUntil(minValidInstant, TimeZone .UTC )
462
+ }
463
+ assertFailsWith<DateTimeArithmeticException > {
464
+ maxValidInstant.periodUntil(minValidInstant - 1 .nanoseconds, TimeZone .UTC )
465
+ }
466
+
467
+ // Instant.until
468
+ // Arithmetic overflow
469
+ assertEquals(Long .MAX_VALUE , minValidInstant.until(maxValidInstant, DateTimeUnit .NANOSECOND , TimeZone .UTC ))
470
+ assertEquals(Long .MIN_VALUE , maxValidInstant.until(minValidInstant, DateTimeUnit .NANOSECOND , TimeZone .UTC ))
471
+ // Overflowing a LocalDateTime in input
472
+ assertFailsWith<DateTimeArithmeticException > {
473
+ (maxValidInstant + 1 .nanoseconds).until(maxValidInstant, DateTimeUnit .NANOSECOND , TimeZone .UTC )
474
+ }
475
+ assertFailsWith<DateTimeArithmeticException > {
476
+ maxValidInstant.until(maxValidInstant + 1 .nanoseconds, DateTimeUnit .NANOSECOND , TimeZone .UTC )
477
+ }
478
+ }
479
+
299
480
}
0 commit comments