1
1
use crate :: time:: Duration ;
2
2
3
+ const SECS_IN_MINUTE : u64 = 60 ;
4
+
3
5
#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
4
6
pub struct Instant ( Duration ) ;
5
7
@@ -70,13 +72,32 @@ impl SystemTime {
70
72
Self ( system_time_internal:: from_uefi ( & t) )
71
73
}
72
74
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
+ }
76
96
}
77
97
78
98
pub fn now ( ) -> SystemTime {
79
99
system_time_internal:: now ( )
100
+ . map ( Self :: from_uefi)
80
101
. unwrap_or_else ( || panic ! ( "time not implemented on this platform" ) )
81
102
}
82
103
@@ -104,12 +125,11 @@ pub(crate) mod system_time_internal {
104
125
use crate :: mem:: MaybeUninit ;
105
126
use crate :: ptr:: NonNull ;
106
127
107
- const SECS_IN_MINUTE : u64 = 60 ;
108
128
const SECS_IN_HOUR : u64 = SECS_IN_MINUTE * 60 ;
109
129
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 ;
111
131
112
- pub fn now ( ) -> Option < SystemTime > {
132
+ pub ( crate ) fn now ( ) -> Option < Time > {
113
133
let runtime_services: NonNull < RuntimeServices > = helpers:: runtime_services ( ) ?;
114
134
let mut t: MaybeUninit < Time > = MaybeUninit :: uninit ( ) ;
115
135
let r = unsafe {
@@ -119,9 +139,7 @@ pub(crate) mod system_time_internal {
119
139
return None ;
120
140
}
121
141
122
- let t = unsafe { t. assume_init ( ) } ;
123
-
124
- Some ( SystemTime :: from_uefi ( t) )
142
+ Some ( unsafe { t. assume_init ( ) } )
125
143
}
126
144
127
145
/// This algorithm is a modified form of the one described in the post
@@ -162,7 +180,7 @@ pub(crate) mod system_time_internal {
162
180
+ ( t. hour as u64 ) * SECS_IN_HOUR ;
163
181
164
182
// 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 ;
166
184
167
185
let epoch: u64 = if t. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
168
186
adjusted_localtime_epoc
@@ -180,16 +198,27 @@ pub(crate) mod system_time_internal {
180
198
///
181
199
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
182
200
/// 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
+
184
205
// Check timzone validity
185
206
assert ! ( timezone <= 1440 && timezone >= -1440 ) ;
186
207
187
208
// FIXME(#126043): use checked_sub_signed once stabilized
209
+ // This cannot fail for valid SystemTime due to SYSTEMTIME_TIMEZONE
188
210
let secs =
189
211
dur. as_secs ( ) . checked_add_signed ( ( -timezone as i64 ) * SECS_IN_MINUTE as i64 ) . unwrap ( ) ;
190
212
191
213
// 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
+ } ;
193
222
194
223
let days = secs / SECS_IN_DAY ;
195
224
let remaining_secs = secs % SECS_IN_DAY ;
@@ -212,9 +241,10 @@ pub(crate) mod system_time_internal {
212
241
let minute = ( ( remaining_secs % SECS_IN_HOUR ) / SECS_IN_MINUTE ) as u8 ;
213
242
let second = ( remaining_secs % SECS_IN_MINUTE ) as u8 ;
214
243
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 {
218
248
year : y as u16 ,
219
249
month : m as u8 ,
220
250
day : d as u8 ,
@@ -228,7 +258,17 @@ pub(crate) mod system_time_internal {
228
258
pad2 : 0 ,
229
259
} )
230
260
} 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)
232
272
}
233
273
}
234
274
}
0 commit comments