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 : i64 = - 1440 * SECS_IN_MINUTE as i64 ;
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
@@ -161,17 +179,15 @@ pub(crate) mod system_time_internal {
161179 + ( t. minute as u64 ) * SECS_IN_MINUTE
162180 + ( t. hour as u64 ) * SECS_IN_HOUR ;
163181
164- // Calculate the offset from 1/1/1900 at timezone -1440 min
165- let adjusted_localtime_epoc: u64 = localtime_epoch + TIMEZONE_DELTA ;
166-
167- let epoch: u64 = if t. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
168- adjusted_localtime_epoc
182+ let normalized_timezone = if t. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
183+ -SYSTEMTIME_TIMEZONE
169184 } else {
170- adjusted_localtime_epoc
171- . checked_add_signed ( ( t. timezone as i64 ) * SECS_IN_MINUTE as i64 )
172- . unwrap ( )
185+ ( t. timezone as i64 ) * SECS_IN_MINUTE as i64 - SYSTEMTIME_TIMEZONE
173186 } ;
174187
188+ // Calculate the offset from 1/1/1900 at timezone -1440 min
189+ let epoch = localtime_epoch. checked_add_signed ( normalized_timezone) . unwrap ( ) ;
190+
175191 Duration :: new ( epoch, t. nanosecond )
176192 }
177193
@@ -180,16 +196,24 @@ pub(crate) mod system_time_internal {
180196 ///
181197 /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
182198 /// epoch used in the original algorithm.
183- pub ( crate ) const fn to_uefi ( dur : & Duration , timezone : i16 , daylight : u8 ) -> Option < Time > {
199+ pub ( crate ) const fn to_uefi ( dur : & Duration , timezone : i16 , daylight : u8 ) -> Result < Time , i16 > {
200+ const MIN_IN_HOUR : u64 = 60 ;
201+ const MIN_IN_DAY : u64 = MIN_IN_HOUR * 24 ;
202+
184203 // Check timezone validity
185204 assert ! ( timezone <= 1440 && timezone >= -1440 ) ;
186205
187- // FIXME(#126043): use checked_sub_signed once stabilized
188- let secs =
189- dur. as_secs ( ) . checked_add_signed ( ( -timezone as i64 ) * SECS_IN_MINUTE as i64 ) . unwrap ( ) ;
190-
191206 // Convert to seconds since 1900-01-01-00:00:00 in timezone.
192- let Some ( secs) = secs. checked_sub ( TIMEZONE_DELTA ) else { return None } ;
207+ let Some ( secs) = dur
208+ . as_secs ( )
209+ . checked_add_signed ( SYSTEMTIME_TIMEZONE - ( timezone as i64 * SECS_IN_MINUTE as i64 ) )
210+ else {
211+ // If the current timezone cannot be used, find the closest timezone that will allow the
212+ // conversion to succeed.
213+ let new_tz = ( dur. as_secs ( ) / SECS_IN_MINUTE ) as i16
214+ + ( SYSTEMTIME_TIMEZONE / SECS_IN_MINUTE as i64 ) as i16 ;
215+ return Err ( new_tz) ;
216+ } ;
193217
194218 let days = secs / SECS_IN_DAY ;
195219 let remaining_secs = secs % SECS_IN_DAY ;
@@ -212,9 +236,10 @@ pub(crate) mod system_time_internal {
212236 let minute = ( ( remaining_secs % SECS_IN_HOUR ) / SECS_IN_MINUTE ) as u8 ;
213237 let second = ( remaining_secs % SECS_IN_MINUTE ) as u8 ;
214238
215- // Check Bounds
216- if y >= 1900 && y <= 9999 {
217- Some ( Time {
239+ // At this point, invalid time will be greater than MAX representable time. It cannot be less
240+ // than minimum time since we already take care of that case above.
241+ if y <= 9999 {
242+ Ok ( Time {
218243 year : y as u16 ,
219244 month : m as u8 ,
220245 day : d as u8 ,
@@ -228,7 +253,17 @@ pub(crate) mod system_time_internal {
228253 pad2 : 0 ,
229254 } )
230255 } else {
231- None
256+ assert ! ( y == 10000 ) ;
257+ assert ! ( m == 1 ) ;
258+
259+ let delta = ( ( d - 1 ) as u64 * MIN_IN_DAY
260+ + hour as u64 * MIN_IN_HOUR
261+ + minute as u64
262+ + if second == 0 { 0 } else { 1 } ) as i16 ;
263+ let new_tz = timezone + delta;
264+
265+ assert ! ( new_tz <= 1440 && new_tz >= -1440 ) ;
266+ Err ( new_tz)
232267 }
233268 }
234269}
0 commit comments