File tree 2 files changed +53
-1
lines changed
2 files changed +53
-1
lines changed Original file line number Diff line number Diff line change @@ -1486,3 +1486,36 @@ fn locale_decimal_point() {
1486
1486
assert_eq ! ( dt. format_localized( "%T%.6f" , ar_SY) . to_string( ) , "18:58:00.123456" ) ;
1487
1487
assert_eq ! ( dt. format_localized( "%T%.9f" , ar_SY) . to_string( ) , "18:58:00.123456780" ) ;
1488
1488
}
1489
+
1490
+ /// This is an extended test for <https://github.com/chronotope/chrono/issues/1289>.
1491
+ #[ test]
1492
+ fn nano_roundrip ( ) {
1493
+ const BILLION : i64 = 1_000_000_000 ;
1494
+
1495
+ for nanos in [
1496
+ i64:: MIN ,
1497
+ i64:: MIN + 1 ,
1498
+ i64:: MIN + 2 ,
1499
+ i64:: MIN + BILLION - 1 ,
1500
+ i64:: MIN + BILLION ,
1501
+ i64:: MIN + BILLION + 1 ,
1502
+ -BILLION - 1 ,
1503
+ -BILLION ,
1504
+ -BILLION + 1 ,
1505
+ 0 ,
1506
+ BILLION - 1 ,
1507
+ BILLION ,
1508
+ BILLION + 1 ,
1509
+ i64:: MAX - BILLION - 1 ,
1510
+ i64:: MAX - BILLION ,
1511
+ i64:: MAX - BILLION + 1 ,
1512
+ i64:: MAX - 2 ,
1513
+ i64:: MAX - 1 ,
1514
+ i64:: MAX ,
1515
+ ] {
1516
+ println ! ( "nanos: {}" , nanos) ;
1517
+ let dt = Utc . timestamp_nanos ( nanos) ;
1518
+ let nanos2 = dt. timestamp_nanos_opt ( ) . expect ( "value roundtrips" ) ;
1519
+ assert_eq ! ( nanos, nanos2) ;
1520
+ }
1521
+ }
Original file line number Diff line number Diff line change @@ -503,7 +503,26 @@ impl NaiveDateTime {
503
503
#[ inline]
504
504
#[ must_use]
505
505
pub fn timestamp_nanos_opt ( & self ) -> Option < i64 > {
506
- self . timestamp ( ) . checked_mul ( 1_000_000_000 ) ?. checked_add ( self . time . nanosecond ( ) as i64 )
506
+ let mut timestamp = self . timestamp ( ) ;
507
+ let mut timestamp_subsec_nanos = i64:: from ( self . timestamp_subsec_nanos ( ) ) ;
508
+
509
+ // subsec nanos are always non-negative, however the timestamp itself (both in seconds and in nanos) can be
510
+ // negative. Now i64::MIN is NOT dividable by 1_000_000_000, so
511
+ //
512
+ // (timestamp * 1_000_000_000) + nanos
513
+ //
514
+ // may underflow (even when in theory we COULD represent the datetime as i64) because we add the non-negative
515
+ // nanos AFTER the multiplication. This is fixed by converting the negative case to
516
+ //
517
+ // ((timestamp + 1) * 1_000_000_000) + (ns - 1_000_000_000)
518
+ //
519
+ // Also see <https://github.com/chronotope/chrono/issues/1289>.
520
+ if timestamp < 0 && timestamp_subsec_nanos > 0 {
521
+ timestamp_subsec_nanos -= 1_000_000_000 ;
522
+ timestamp += 1 ;
523
+ }
524
+
525
+ timestamp. checked_mul ( 1_000_000_000 ) . and_then ( |ns| ns. checked_add ( timestamp_subsec_nanos) )
507
526
}
508
527
509
528
/// Returns the number of milliseconds since the last whole non-leap second.
You can’t perform that action at this time.
0 commit comments