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