1+ use crate :: cmp:: Ordering ;
12use crate :: time:: Duration ;
23
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-
74#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
85pub struct Instant ( Duration ) ;
96
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+ }
1233
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+ } ;
1453
1554impl Instant {
1655 pub fn now ( ) -> Instant {
@@ -40,21 +79,61 @@ impl Instant {
4079}
4180
4281impl 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+
43113 pub fn now ( ) -> SystemTime {
44114 system_time_internal:: now ( )
45115 . unwrap_or_else ( || panic ! ( "time not implemented on this platform" ) )
46116 }
47117
48118 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) )
50121 }
51122
52123 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+ )
54129 }
55130
56131 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+ )
58137 }
59138}
60139
@@ -63,9 +142,177 @@ pub(crate) mod system_time_internal {
63142
64143 use super :: super :: helpers;
65144 use super :: * ;
145+ use crate :: cmp:: Ordering ;
66146 use crate :: mem:: MaybeUninit ;
67147 use crate :: ptr:: NonNull ;
68148
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+
69316 pub fn now ( ) -> Option < SystemTime > {
70317 let runtime_services: NonNull < RuntimeServices > = helpers:: runtime_services ( ) ?;
71318 let mut t: MaybeUninit < Time > = MaybeUninit :: uninit ( ) ;
@@ -79,38 +326,7 @@ pub(crate) mod system_time_internal {
79326
80327 let t = unsafe { t. assume_init ( ) } ;
81328
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) )
114330 }
115331}
116332
0 commit comments