@@ -104,7 +104,7 @@ impl TryFrom<Duration> for time::Duration {
104104 /// Converts a `Duration` to a `std::time::Duration`, failing if the duration is negative.
105105 fn try_from ( mut duration : Duration ) -> Result < time:: Duration , DurationError > {
106106 duration. normalize ( ) ;
107- if duration. seconds >= 0 {
107+ if duration. seconds >= 0 && duration . nanos >= 0 {
108108 Ok ( time:: Duration :: new (
109109 duration. seconds as u64 ,
110110 duration. nanos as u32 ,
@@ -454,6 +454,51 @@ mod tests {
454454 )
455455 }
456456 }
457+
458+ #[ test]
459+ fn check_duration_roundtrip_nanos(
460+ nanos in u32 :: arbitrary( ) ,
461+ ) {
462+ let seconds = 0 ;
463+ let std_duration = std:: time:: Duration :: new( seconds, nanos) ;
464+ let prost_duration = match Duration :: try_from( std_duration) {
465+ Ok ( duration) => duration,
466+ Err ( _) => return Err ( TestCaseError :: reject( "duration out of range" ) ) ,
467+ } ;
468+ prop_assert_eq!( time:: Duration :: try_from( prost_duration. clone( ) ) . unwrap( ) , std_duration) ;
469+
470+ if std_duration != time:: Duration :: default ( ) {
471+ let neg_prost_duration = Duration {
472+ seconds: -prost_duration. seconds,
473+ nanos: -prost_duration. nanos,
474+ } ;
475+
476+ prop_assert!(
477+ matches!(
478+ time:: Duration :: try_from( neg_prost_duration) ,
479+ Err ( DurationError :: NegativeDuration ( d) ) if d == std_duration,
480+ )
481+ )
482+ }
483+ }
484+ }
485+
486+ #[ cfg( feature = "std" ) ]
487+ #[ test]
488+ fn check_duration_try_from_negative_nanos ( ) {
489+ let seconds: u64 = 0 ;
490+ let nanos: u32 = 1 ;
491+ let std_duration = std:: time:: Duration :: new ( seconds, nanos) ;
492+
493+ let neg_prost_duration = Duration {
494+ seconds : 0 ,
495+ nanos : -1 ,
496+ } ;
497+
498+ assert ! ( matches!(
499+ time:: Duration :: try_from( neg_prost_duration) ,
500+ Err ( DurationError :: NegativeDuration ( d) ) if d == std_duration,
501+ ) )
457502 }
458503
459504 #[ cfg( feature = "std" ) ]
0 commit comments