@@ -203,7 +203,15 @@ impl Tai64N {
203203 }
204204 }
205205
206- /// Convert `TAI64N`to `SystemTime`.
206+ /// Convert `TAI64N` to `SystemTime`.
207+ ///
208+ /// # Panics
209+ ///
210+ /// Panics if the timestamp cannot be represented as `SystemTime`. This can
211+ /// occur when the `Tai64N` value is outside the range representable by the
212+ /// platform's `SystemTime` (typically backed by `i64` seconds from Unix epoch).
213+ ///
214+ /// For a non-panicking alternative, use `SystemTime::try_from(tai64n)`.
207215 #[ cfg( feature = "std" ) ]
208216 pub fn to_system_time ( self ) -> SystemTime {
209217 match self . duration_since ( & Self :: UNIX_EPOCH ) {
@@ -265,6 +273,19 @@ impl From<SystemTime> for Tai64N {
265273 }
266274}
267275
276+ #[ cfg( feature = "std" ) ]
277+ impl TryFrom < Tai64N > for SystemTime {
278+ type Error = Error ;
279+
280+ fn try_from ( tai : Tai64N ) -> Result < Self , Self :: Error > {
281+ match tai. duration_since ( & Tai64N :: UNIX_EPOCH ) {
282+ Ok ( d) => UNIX_EPOCH . checked_add ( d) ,
283+ Err ( d) => UNIX_EPOCH . checked_sub ( d) ,
284+ }
285+ . ok_or ( Error :: TimestampOverflow )
286+ }
287+ }
288+
268289#[ allow( clippy:: suspicious_arithmetic_impl) ]
269290impl ops:: Add < Duration > for Tai64N {
270291 type Output = Self ;
@@ -320,13 +341,17 @@ pub enum Error {
320341
321342 /// Nanosecond part must be <= 999999999.
322343 NanosInvalid ,
344+
345+ /// Timestamp cannot be represented as `SystemTime` (overflow/underflow).
346+ TimestampOverflow ,
323347}
324348
325349impl fmt:: Display for Error {
326350 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
327351 let description = match self {
328352 Error :: LengthInvalid => "length invalid" ,
329353 Error :: NanosInvalid => "invalid number of nanoseconds" ,
354+ Error :: TimestampOverflow => "timestamp cannot be represented as SystemTime" ,
330355 } ;
331356
332357 write ! ( f, "{description}" )
@@ -360,4 +385,42 @@ mod tests {
360385
361386 assert_eq ! ( t, t1) ;
362387 }
388+
389+ #[ test]
390+ #[ should_panic( expected = "overflow when adding duration to instant" ) ]
391+ fn to_system_time_panics_from_slice ( ) {
392+ let malicious_timestamp: [ u8 ; 12 ] = [
393+ 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ,
394+ 0x00 , 0x00 , 0x00 , 0x00 ,
395+ ] ;
396+
397+ let timestamp = Tai64N :: from_slice ( & malicious_timestamp) . unwrap ( ) ;
398+ let _ = timestamp. to_system_time ( ) ; // panics here
399+ }
400+
401+ #[ test]
402+ fn try_into_system_time_success ( ) {
403+ let tai = Tai64N :: now ( ) ;
404+ let result: Result < SystemTime , _ > = tai. try_into ( ) ;
405+ assert ! ( result. is_ok( ) ) ;
406+ }
407+
408+ #[ test]
409+ fn try_into_system_time_overflow ( ) {
410+ let malicious: [ u8 ; 12 ] = [
411+ 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ,
412+ 0x00 , 0x00 , 0x00 , 0x00 ,
413+ ] ;
414+ let tai = Tai64N :: from_slice ( & malicious) . unwrap ( ) ;
415+ let result: Result < SystemTime , _ > = tai. try_into ( ) ;
416+ assert_eq ! ( result, Err ( Error :: TimestampOverflow ) ) ;
417+ }
418+
419+ #[ test]
420+ fn try_into_system_time_roundtrip ( ) {
421+ let original = SystemTime :: now ( ) ;
422+ let tai = Tai64N :: from ( original) ;
423+ let recovered: SystemTime = tai. try_into ( ) . expect ( "should be representable" ) ;
424+ assert_eq ! ( original, recovered) ;
425+ }
363426}
0 commit comments