File tree Expand file tree Collapse file tree 2 files changed +53
-1
lines changed Expand file tree Collapse file tree 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() {
14861486 assert_eq ! ( dt. format_localized( "%T%.6f" , ar_SY) . to_string( ) , "18:58:00.123456" ) ;
14871487 assert_eq ! ( dt. format_localized( "%T%.9f" , ar_SY) . to_string( ) , "18:58:00.123456780" ) ;
14881488}
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 {
503503 #[ inline]
504504 #[ must_use]
505505 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) )
507526 }
508527
509528 /// Returns the number of milliseconds since the last whole non-leap second.
You can’t perform that action at this time.
0 commit comments