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