11use  crate :: time:: Duration ; 
22
3+ const  SECS_IN_MINUTE :  u64  = 60 ; 
4+ 
35#[ derive( Copy ,  Clone ,  PartialEq ,  Eq ,  PartialOrd ,  Ord ,  Debug ,  Hash ) ]  
46pub  struct  Instant ( Duration ) ; 
57
@@ -70,13 +72,32 @@ impl SystemTime {
7072        Self ( system_time_internal:: from_uefi ( & t) ) 
7173    } 
7274
73-     #[ expect( dead_code) ]  
74-     pub ( crate )  const  fn  to_uefi ( self ,  timezone :  i16 ,  daylight :  u8 )  -> Option < r_efi:: efi:: Time >  { 
75-         system_time_internal:: to_uefi ( & self . 0 ,  timezone,  daylight) 
75+     pub ( crate )  const  fn  to_uefi ( 
76+         self , 
77+         timezone :  i16 , 
78+         daylight :  u8 , 
79+     )  -> Result < r_efi:: efi:: Time ,  i16 >  { 
80+         // system_time_internal::to_uefi requires a valid timezone. In case of unspecified timezone, 
81+         // we just pass 0 since it is assumed that no timezone related adjustments are required. 
82+         if  timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE  { 
83+             system_time_internal:: to_uefi ( & self . 0 ,  0 ,  daylight) 
84+         }  else  { 
85+             system_time_internal:: to_uefi ( & self . 0 ,  timezone,  daylight) 
86+         } 
87+     } 
88+ 
89+     /// Create UEFI Time with the closest timezone (minute offset) that still allows the time to be 
90+ /// represented. 
91+ pub ( crate )  fn  to_uefi_loose ( self ,  timezone :  i16 ,  daylight :  u8 )  -> r_efi:: efi:: Time  { 
92+         match  self . to_uefi ( timezone,  daylight)  { 
93+             Ok ( x)  => x, 
94+             Err ( tz)  => self . to_uefi ( tz,  daylight) . unwrap ( ) , 
95+         } 
7696    } 
7797
7898    pub  fn  now ( )  -> SystemTime  { 
7999        system_time_internal:: now ( ) 
100+             . map ( Self :: from_uefi) 
80101            . unwrap_or_else ( || panic ! ( "time not implemented on this platform" ) ) 
81102    } 
82103
@@ -104,12 +125,11 @@ pub(crate) mod system_time_internal {
104125    use  crate :: mem:: MaybeUninit ; 
105126    use  crate :: ptr:: NonNull ; 
106127
107-     const  SECS_IN_MINUTE :  u64  = 60 ; 
108128    const  SECS_IN_HOUR :  u64  = SECS_IN_MINUTE  *  60 ; 
109129    const  SECS_IN_DAY :  u64  = SECS_IN_HOUR  *  24 ; 
110-     const  TIMEZONE_DELTA :  u64  = 1440  *  SECS_IN_MINUTE ; 
130+     const  SYSTEMTIME_TIMEZONE :  u64  = 1440  *  SECS_IN_MINUTE ; 
111131
112-     pub  fn  now ( )  -> Option < SystemTime >  { 
132+     pub ( crate )  fn  now ( )  -> Option < Time >  { 
113133        let  runtime_services:  NonNull < RuntimeServices >  = helpers:: runtime_services ( ) ?; 
114134        let  mut  t:  MaybeUninit < Time >  = MaybeUninit :: uninit ( ) ; 
115135        let  r = unsafe  { 
@@ -119,9 +139,7 @@ pub(crate) mod system_time_internal {
119139            return  None ; 
120140        } 
121141
122-         let  t = unsafe  {  t. assume_init ( )  } ; 
123- 
124-         Some ( SystemTime :: from_uefi ( t) ) 
142+         Some ( unsafe  {  t. assume_init ( )  } ) 
125143    } 
126144
127145    /// This algorithm is a modified form of the one described in the post 
@@ -162,7 +180,7 @@ pub(crate) mod system_time_internal {
162180            + ( t. hour  as  u64 )  *  SECS_IN_HOUR ; 
163181
164182        // Calculate the offset from 1/1/1900 at timezone -1440 min 
165-         let  adjusted_localtime_epoc:  u64  = localtime_epoch + TIMEZONE_DELTA ; 
183+         let  adjusted_localtime_epoc:  u64  = localtime_epoch + SYSTEMTIME_TIMEZONE ; 
166184
167185        let  epoch:  u64  = if  t. timezone  == r_efi:: efi:: UNSPECIFIED_TIMEZONE  { 
168186            adjusted_localtime_epoc
@@ -180,16 +198,27 @@ pub(crate) mod system_time_internal {
180198/// 
181199/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX 
182200/// epoch used in the original algorithm. 
183- pub ( crate )  const  fn  to_uefi ( dur :  & Duration ,  timezone :  i16 ,  daylight :  u8 )  -> Option < Time >  { 
201+ pub ( crate )  const  fn  to_uefi ( dur :  & Duration ,  timezone :  i16 ,  daylight :  u8 )  -> Result < Time ,  i16 >  { 
202+         const  MIN_IN_HOUR :  u64  = 60 ; 
203+         const  MIN_IN_DAY :  u64  = MIN_IN_HOUR  *  24 ; 
204+ 
184205        // Check timzone validity 
185206        assert ! ( timezone <= 1440  && timezone >= -1440 ) ; 
186207
187208        // FIXME(#126043): use checked_sub_signed once stabilized 
209+         // This cannot fail for valid SystemTime due to SYSTEMTIME_TIMEZONE 
188210        let  secs =
189211            dur. as_secs ( ) . checked_add_signed ( ( -timezone as  i64 )  *  SECS_IN_MINUTE  as  i64 ) . unwrap ( ) ; 
190212
191213        // Convert to seconds since 1900-01-01-00:00:00 in timezone. 
192-         let  Some ( secs)  = secs. checked_sub ( TIMEZONE_DELTA )  else  {  return  None  } ; 
214+         let  Some ( secs)  = secs. checked_sub ( SYSTEMTIME_TIMEZONE )  else  { 
215+             // If the current timezone cannot be used, find the closest timezone that will allow the 
216+             // conversion to succeed. 
217+             let  delta = SYSTEMTIME_TIMEZONE  - secs; 
218+             let  new_tz = timezone
219+                 - ( delta / SECS_IN_MINUTE  + if  delta % SECS_IN_MINUTE  == 0  {  0  }  else  {  1  } )  as  i16 ; 
220+             return  Err ( new_tz) ; 
221+         } ; 
193222
194223        let  days = secs / SECS_IN_DAY ; 
195224        let  remaining_secs = secs % SECS_IN_DAY ; 
@@ -212,9 +241,10 @@ pub(crate) mod system_time_internal {
212241        let  minute = ( ( remaining_secs % SECS_IN_HOUR )  / SECS_IN_MINUTE )  as  u8 ; 
213242        let  second = ( remaining_secs % SECS_IN_MINUTE )  as  u8 ; 
214243
215-         // Check Bounds 
216-         if  y >= 1900  && y <= 9999  { 
217-             Some ( Time  { 
244+         // At this point, invalid time will be greater than MAX representable time. It cannot be less 
245+         // than minimum time since we already take care of that case above. 
246+         if  y <= 9999  { 
247+             Ok ( Time  { 
218248                year :  y as  u16 , 
219249                month :  m as  u8 , 
220250                day :  d as  u8 , 
@@ -228,7 +258,17 @@ pub(crate) mod system_time_internal {
228258                pad2 :  0 , 
229259            } ) 
230260        }  else  { 
231-             None 
261+             assert ! ( y == 10000 ) ; 
262+             assert ! ( m == 1 ) ; 
263+ 
264+             let  delta = ( ( d - 1 )  as  u64  *  MIN_IN_DAY 
265+                 + hour as  u64  *  MIN_IN_HOUR 
266+                 + minute as  u64 
267+                 + if  second == 0  {  0  }  else  {  1  } )  as  i16 ; 
268+             let  new_tz = timezone + delta; 
269+ 
270+             assert ! ( new_tz <= 1440  && new_tz >= -1440 ) ; 
271+             Err ( new_tz) 
232272        } 
233273    } 
234274} 
0 commit comments