@@ -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}" )
@@ -337,6 +362,7 @@ impl fmt::Display for Error {
337362impl std:: error:: Error for Error { }
338363
339364#[ cfg( all( test, feature = "std" ) ) ]
365+ #[ allow( clippy:: unwrap_used) ]
340366mod tests {
341367 use super :: * ;
342368
@@ -360,4 +386,40 @@ mod tests {
360386
361387 assert_eq ! ( t, t1) ;
362388 }
389+
390+ #[ test]
391+ #[ should_panic( expected = "overflow when adding duration to instant" ) ]
392+ fn to_system_time_panics_from_slice ( ) {
393+ let malicious_timestamp: [ u8 ; 12 ] = [
394+ 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 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 , 0x00 , 0x00 , 0x00 , 0x00 ,
412+ ] ;
413+ let tai = Tai64N :: from_slice ( & malicious) . unwrap ( ) ;
414+ let result: Result < SystemTime , _ > = tai. try_into ( ) ;
415+ assert_eq ! ( result, Err ( Error :: TimestampOverflow ) ) ;
416+ }
417+
418+ #[ test]
419+ fn try_into_system_time_roundtrip ( ) {
420+ let original = SystemTime :: now ( ) ;
421+ let tai = Tai64N :: from ( original) ;
422+ let recovered: SystemTime = tai. try_into ( ) . expect ( "should be representable" ) ;
423+ assert_eq ! ( original, recovered) ;
424+ }
363425}
0 commit comments