1
+ use crate :: cmp:: Ordering ;
1
2
use crate :: time:: Duration ;
2
3
3
- const SECS_IN_MINUTE : u64 = 60 ;
4
- const SECS_IN_HOUR : u64 = SECS_IN_MINUTE * 60 ;
5
- const SECS_IN_DAY : u64 = SECS_IN_HOUR * 24 ;
6
-
7
4
#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
8
5
pub struct Instant ( Duration ) ;
9
6
10
- #[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
11
- pub struct SystemTime ( Duration ) ;
7
+ #[ derive( Copy , Clone , Debug , Hash , Eq ) ]
8
+ pub struct SystemTime {
9
+ year : u16 ,
10
+ month : u8 ,
11
+ day : u8 ,
12
+ hour : u8 ,
13
+ minute : u8 ,
14
+ second : u8 ,
15
+ nanosecond : u32 ,
16
+ timezone : i16 ,
17
+ daylight : u8 ,
18
+ }
19
+
20
+ /// Deriving does not work because we need to account for timezone
21
+ impl Ord for SystemTime {
22
+ fn cmp ( & self , other : & Self ) -> Ordering {
23
+ system_time_internal:: UnixTime :: from_systemtime ( self )
24
+ . cmp ( & system_time_internal:: UnixTime :: from_systemtime ( other) )
25
+ }
26
+ }
27
+
28
+ impl PartialOrd for SystemTime {
29
+ fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
30
+ Some ( self . cmp ( other) )
31
+ }
32
+ }
12
33
13
- pub const UNIX_EPOCH : SystemTime = SystemTime ( Duration :: from_secs ( 0 ) ) ;
34
+ /// Deriving does not work because we need to account for timezone
35
+ impl PartialEq for SystemTime {
36
+ fn eq ( & self , other : & Self ) -> bool {
37
+ system_time_internal:: UnixTime :: from_systemtime ( self )
38
+ == system_time_internal:: UnixTime :: from_systemtime ( other)
39
+ }
40
+ }
41
+
42
+ pub const UNIX_EPOCH : SystemTime = SystemTime {
43
+ year : 1970 ,
44
+ month : 1 ,
45
+ day : 1 ,
46
+ hour : 0 ,
47
+ minute : 0 ,
48
+ second : 0 ,
49
+ nanosecond : 0 ,
50
+ timezone : 0 ,
51
+ daylight : 0 ,
52
+ } ;
14
53
15
54
impl Instant {
16
55
pub fn now ( ) -> Instant {
@@ -40,21 +79,61 @@ impl Instant {
40
79
}
41
80
42
81
impl SystemTime {
82
+ pub ( crate ) const fn from_uefi ( t : & r_efi:: efi:: Time ) -> Self {
83
+ Self {
84
+ year : t. year ,
85
+ month : t. month ,
86
+ day : t. day ,
87
+ hour : t. hour ,
88
+ minute : t. minute ,
89
+ second : t. second ,
90
+ nanosecond : t. nanosecond ,
91
+ timezone : t. timezone ,
92
+ daylight : t. daylight ,
93
+ }
94
+ }
95
+
96
+ #[ expect( dead_code) ]
97
+ pub ( crate ) const fn to_uefi ( & self ) -> r_efi:: efi:: Time {
98
+ r_efi:: efi:: Time {
99
+ year : self . year ,
100
+ month : self . month ,
101
+ day : self . day ,
102
+ hour : self . hour ,
103
+ minute : self . minute ,
104
+ second : self . second ,
105
+ nanosecond : self . nanosecond ,
106
+ timezone : self . timezone ,
107
+ daylight : self . daylight ,
108
+ pad1 : 0 ,
109
+ pad2 : 0 ,
110
+ }
111
+ }
112
+
43
113
pub fn now ( ) -> SystemTime {
44
114
system_time_internal:: now ( )
45
115
. unwrap_or_else ( || panic ! ( "time not implemented on this platform" ) )
46
116
}
47
117
48
118
pub fn sub_time ( & self , other : & SystemTime ) -> Result < Duration , Duration > {
49
- self . 0 . checked_sub ( other. 0 ) . ok_or_else ( || other. 0 - self . 0 )
119
+ system_time_internal:: UnixTime :: from_systemtime ( self )
120
+ . sub_time ( system_time_internal:: UnixTime :: from_systemtime ( other) )
50
121
}
51
122
52
123
pub fn checked_add_duration ( & self , other : & Duration ) -> Option < SystemTime > {
53
- Some ( SystemTime ( self . 0 . checked_add ( * other) ?) )
124
+ Some (
125
+ system_time_internal:: UnixTime :: from_systemtime ( self )
126
+ . checked_add ( * other)
127
+ . to_systemtime ( self . timezone , self . daylight ) ,
128
+ )
54
129
}
55
130
56
131
pub fn checked_sub_duration ( & self , other : & Duration ) -> Option < SystemTime > {
57
- Some ( SystemTime ( self . 0 . checked_sub ( * other) ?) )
132
+ Some (
133
+ system_time_internal:: UnixTime :: from_systemtime ( self )
134
+ . checked_sub ( * other)
135
+ . to_systemtime ( self . timezone , self . daylight ) ,
136
+ )
58
137
}
59
138
}
60
139
@@ -63,9 +142,177 @@ pub(crate) mod system_time_internal {
63
142
64
143
use super :: super :: helpers;
65
144
use super :: * ;
145
+ use crate :: cmp:: Ordering ;
66
146
use crate :: mem:: MaybeUninit ;
67
147
use crate :: ptr:: NonNull ;
68
148
149
+ const SECS_IN_MINUTE : i64 = 60 ;
150
+ const SECS_IN_HOUR : i64 = SECS_IN_MINUTE * 60 ;
151
+ const SECS_IN_DAY : i64 = SECS_IN_HOUR * 24 ;
152
+ const NS_PER_SEC : u32 = 1_000_000_000 ;
153
+
154
+ #[ derive( Eq , PartialEq ) ]
155
+ pub ( crate ) struct UnixTime {
156
+ secs : i64 ,
157
+ nanos : u32 ,
158
+ }
159
+
160
+ impl UnixTime {
161
+ // This algorithm is based on the one described in the post
162
+ // https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
163
+ pub ( crate ) const fn from_systemtime ( t : & super :: SystemTime ) -> Self {
164
+ assert ! ( t. month <= 12 ) ;
165
+ assert ! ( t. month != 0 ) ;
166
+
167
+ const YEAR_BASE : u32 = 4800 ; /* Before min year, multiple of 400. */
168
+
169
+ // Calculate the number of days since 1/1/1970
170
+ // Use 1 March as the start
171
+ let ( m_adj, overflow) : ( u32 , bool ) = ( t. month as u32 ) . overflowing_sub ( 3 ) ;
172
+ let ( carry, adjust) : ( u32 , u32 ) = if overflow { ( 1 , 12 ) } else { ( 0 , 0 ) } ;
173
+ let y_adj: u32 = ( t. year as u32 ) + YEAR_BASE - carry;
174
+ let month_days: u32 = ( m_adj. wrapping_add ( adjust) * 62719 + 769 ) / 2048 ;
175
+ let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400 ;
176
+
177
+ // Allow days to be negative to denote days before EPOCH
178
+ let days: i64 =
179
+ ( y_adj * 365 + leap_days + month_days + ( t. day as u32 - 1 ) ) as i64 - 2472632 ;
180
+
181
+ let localtime_epoch: i64 = days * SECS_IN_DAY
182
+ + ( t. second as i64 )
183
+ + ( t. minute as i64 ) * SECS_IN_MINUTE
184
+ + ( t. hour as i64 ) * SECS_IN_HOUR ;
185
+
186
+ let utc_epoch: i64 = if t. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
187
+ localtime_epoch
188
+ } else {
189
+ localtime_epoch + ( t. timezone as i64 ) * SECS_IN_MINUTE
190
+ } ;
191
+
192
+ Self { secs : utc_epoch, nanos : t. nanosecond }
193
+ }
194
+
195
+ /// This algorithm is taken from: http://howardhinnant.github.io/date_algorithms.html
196
+ pub ( crate ) const fn to_systemtime ( & self , timezone : i16 , daylight : u8 ) -> super :: SystemTime {
197
+ let secs: i64 = if timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
198
+ self . secs
199
+ } else {
200
+ self . secs - ( timezone as i64 ) * SECS_IN_MINUTE
201
+ } ;
202
+
203
+ let ( days, remaining_secs) : ( i64 , u64 ) = {
204
+ let days = secs / SECS_IN_DAY ;
205
+ let remaining_secs = secs % SECS_IN_DAY ;
206
+
207
+ if remaining_secs < 0 {
208
+ ( days - 1 , ( SECS_IN_DAY + remaining_secs) as u64 )
209
+ } else {
210
+ ( days, remaining_secs as u64 )
211
+ }
212
+ } ;
213
+
214
+ let z = days + 719468 ;
215
+ let era = z / 146097 ;
216
+ let doe = z - ( era * 146097 ) ;
217
+ let yoe = ( doe - doe / 1460 + doe / 36524 - doe / 146096 ) / 365 ;
218
+ let mut y = yoe + era * 400 ;
219
+ let doy = doe - ( 365 * yoe + yoe / 4 - yoe / 100 ) ;
220
+ let mp = ( 5 * doy + 2 ) / 153 ;
221
+ let d = doy - ( 153 * mp + 2 ) / 5 + 1 ;
222
+ let m = if mp < 10 { mp + 3 } else { mp - 9 } ;
223
+
224
+ if m <= 2 {
225
+ y += 1 ;
226
+ }
227
+
228
+ let hour = ( remaining_secs / SECS_IN_HOUR as u64 ) as u8 ;
229
+ let minute = ( ( remaining_secs % SECS_IN_HOUR as u64 ) / SECS_IN_MINUTE as u64 ) as u8 ;
230
+ let second = ( remaining_secs % SECS_IN_MINUTE as u64 ) as u8 ;
231
+
232
+ super :: SystemTime {
233
+ year : y as u16 ,
234
+ month : m as u8 ,
235
+ day : d as u8 ,
236
+ hour,
237
+ minute,
238
+ second,
239
+ nanosecond : self . nanos ,
240
+ timezone,
241
+ daylight,
242
+ }
243
+ }
244
+
245
+ pub ( crate ) const fn checked_add ( & self , dur : Duration ) -> Self {
246
+ let temp: u32 = self . nanos + dur. subsec_nanos ( ) ;
247
+ let nanos: u32 = temp % NS_PER_SEC ;
248
+ let secs: i64 = self . secs + dur. as_secs ( ) as i64 + ( temp / NS_PER_SEC ) as i64 ;
249
+
250
+ Self { secs, nanos }
251
+ }
252
+
253
+ pub ( crate ) const fn checked_sub ( & self , dur : Duration ) -> Self {
254
+ let ( secs, nanos) = if self . nanos < dur. subsec_nanos ( ) {
255
+ let temp = NS_PER_SEC + self . nanos - dur. subsec_nanos ( ) ;
256
+ ( self . secs - dur. as_secs ( ) as i64 - 1 , temp)
257
+ } else {
258
+ ( self . secs - dur. as_secs ( ) as i64 , self . nanos - dur. subsec_nanos ( ) )
259
+ } ;
260
+
261
+ Self { secs, nanos }
262
+ }
263
+
264
+ pub ( crate ) fn sub_time ( self , other : Self ) -> Result < Duration , Duration > {
265
+ if self >= other {
266
+ let temp = self - other;
267
+ assert ! ( temp. secs > 0 ) ;
268
+
269
+ Ok ( Duration :: new ( temp. secs as u64 , temp. nanos ) )
270
+ } else {
271
+ let temp = other - self ;
272
+ assert ! ( temp. secs > 0 ) ;
273
+
274
+ Err ( Duration :: new ( temp. secs as u64 , temp. nanos ) )
275
+ }
276
+ }
277
+ }
278
+
279
+ impl Ord for UnixTime {
280
+ fn cmp ( & self , other : & Self ) -> Ordering {
281
+ if self . secs > other. secs {
282
+ Ordering :: Greater
283
+ } else if self . secs < other. secs {
284
+ Ordering :: Less
285
+ } else if self . nanos > other. nanos {
286
+ Ordering :: Greater
287
+ } else if self . nanos < other. nanos {
288
+ Ordering :: Less
289
+ } else {
290
+ Ordering :: Equal
291
+ }
292
+ }
293
+ }
294
+
295
+ impl PartialOrd for UnixTime {
296
+ fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
297
+ Some ( self . cmp ( other) )
298
+ }
299
+ }
300
+
301
+ impl crate :: ops:: Sub for UnixTime {
302
+ type Output = Self ;
303
+
304
+ fn sub ( self , other : Self ) -> Self {
305
+ let ( secs, nanos) = if self . nanos < other. nanos {
306
+ let temp = NS_PER_SEC + self . nanos - other. nanos ;
307
+ ( self . secs - other. secs - 1 , temp)
308
+ } else {
309
+ ( self . secs - other. secs , self . nanos - other. nanos )
310
+ } ;
311
+
312
+ Self { secs, nanos }
313
+ }
314
+ }
315
+
69
316
pub fn now ( ) -> Option < SystemTime > {
70
317
let runtime_services: NonNull < RuntimeServices > = helpers:: runtime_services ( ) ?;
71
318
let mut t: MaybeUninit < Time > = MaybeUninit :: uninit ( ) ;
@@ -79,38 +326,7 @@ pub(crate) mod system_time_internal {
79
326
80
327
let t = unsafe { t. assume_init ( ) } ;
81
328
82
- Some ( SystemTime ( uefi_time_to_duration ( t) ) )
83
- }
84
-
85
- // This algorithm is based on the one described in the post
86
- // https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
87
- pub ( crate ) const fn uefi_time_to_duration ( t : r_efi:: system:: Time ) -> Duration {
88
- assert ! ( t. month <= 12 ) ;
89
- assert ! ( t. month != 0 ) ;
90
-
91
- const YEAR_BASE : u32 = 4800 ; /* Before min year, multiple of 400. */
92
-
93
- // Calculate the number of days since 1/1/1970
94
- // Use 1 March as the start
95
- let ( m_adj, overflow) : ( u32 , bool ) = ( t. month as u32 ) . overflowing_sub ( 3 ) ;
96
- let ( carry, adjust) : ( u32 , u32 ) = if overflow { ( 1 , 12 ) } else { ( 0 , 0 ) } ;
97
- let y_adj: u32 = ( t. year as u32 ) + YEAR_BASE - carry;
98
- let month_days: u32 = ( m_adj. wrapping_add ( adjust) * 62719 + 769 ) / 2048 ;
99
- let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400 ;
100
- let days: u32 = y_adj * 365 + leap_days + month_days + ( t. day as u32 - 1 ) - 2472632 ;
101
-
102
- let localtime_epoch: u64 = ( days as u64 ) * SECS_IN_DAY
103
- + ( t. second as u64 )
104
- + ( t. minute as u64 ) * SECS_IN_MINUTE
105
- + ( t. hour as u64 ) * SECS_IN_HOUR ;
106
-
107
- let utc_epoch: u64 = if t. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
108
- localtime_epoch
109
- } else {
110
- ( localtime_epoch as i64 + ( t. timezone as i64 ) * SECS_IN_MINUTE as i64 ) as u64
111
- } ;
112
-
113
- Duration :: new ( utc_epoch, t. nanosecond )
329
+ Some ( SystemTime :: from_uefi ( & t) )
114
330
}
115
331
}
116
332
0 commit comments