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 {
9+ // Duration from 1900-01-01
10+ dur : Duration ,
11+ timezone : i16 ,
12+ daylight : u8 ,
13+ }
14+
15+ /// Deriving does not work because we need to account for timezone
16+ impl Ord for SystemTime {
17+ fn cmp ( & self , other : & Self ) -> Ordering {
18+ // Comparison between unspecified timezone and normal timezone is not allowed
19+ if ( self . timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE
20+ || other. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE )
21+ && ( self . timezone != other. timezone )
22+ {
23+ panic ! ( "Cannot compare unspecified timezone with a normal timezone" )
24+ } else {
25+ self . dur . cmp ( & other. dur )
26+ }
27+ }
28+ }
29+
30+ impl PartialOrd for SystemTime {
31+ fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
32+ Some ( self . cmp ( other) )
33+ }
34+ }
35+
36+ /// Deriving does not work because we need to account for timezone
37+ impl PartialEq for SystemTime {
38+ fn eq ( & self , other : & Self ) -> bool {
39+ if ( self . timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE
40+ || other. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE )
41+ && ( self . timezone != other. timezone )
42+ {
43+ false
44+ } else {
45+ self . dur . eq ( & other. dur )
46+ }
47+ }
48+ }
49+
50+ impl Eq for SystemTime { }
51+
52+ impl crate :: hash:: Hash for SystemTime {
53+ fn hash < H : crate :: hash:: Hasher > ( & self , state : & mut H ) {
54+ self . dur . hash ( state) ;
55+ }
56+ }
1257
13- pub const UNIX_EPOCH : SystemTime = SystemTime ( Duration :: from_secs ( 0 ) ) ;
58+ pub const UNIX_EPOCH : SystemTime = SystemTime :: from_uefi ( r_efi:: efi:: Time {
59+ year : 1970 ,
60+ month : 1 ,
61+ day : 1 ,
62+ hour : 0 ,
63+ minute : 0 ,
64+ second : 0 ,
65+ nanosecond : 0 ,
66+ timezone : 0 ,
67+ daylight : 0 ,
68+ pad1 : 0 ,
69+ pad2 : 0 ,
70+ } ) ;
1471
1572impl Instant {
1673 pub fn now ( ) -> Instant {
@@ -40,21 +97,43 @@ impl Instant {
4097}
4198
4299impl SystemTime {
100+ pub ( crate ) const fn from_uefi ( t : r_efi:: efi:: Time ) -> Self {
101+ let dur = system_time_internal:: from_uefi ( & t) ;
102+ Self { dur, timezone : t. timezone , daylight : t. daylight }
103+ }
104+
105+ pub ( crate ) const fn to_uefi ( self ) -> Option < r_efi:: efi:: Time > {
106+ system_time_internal:: to_uefi ( & self . dur , self . timezone , self . daylight )
107+ }
108+
43109 pub fn now ( ) -> SystemTime {
44110 system_time_internal:: now ( )
45111 . unwrap_or_else ( || panic ! ( "time not implemented on this platform" ) )
46112 }
47113
48114 pub fn sub_time ( & self , other : & SystemTime ) -> Result < Duration , Duration > {
49- self . 0 . checked_sub ( other. 0 ) . ok_or_else ( || other. 0 - self . 0 )
115+ if ( self . timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE
116+ || other. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE )
117+ && ( self . timezone != other. timezone )
118+ {
119+ panic ! ( "Cannot compare unspecified timezone with a normal timezone" )
120+ } else {
121+ self . dur . checked_sub ( other. dur ) . ok_or_else ( || other. dur - self . dur )
122+ }
50123 }
51124
52125 pub fn checked_add_duration ( & self , other : & Duration ) -> Option < SystemTime > {
53- Some ( SystemTime ( self . 0 . checked_add ( * other) ?) )
126+ let mut temp = self . clone ( ) ;
127+ temp. dur = self . dur . checked_add ( * other) ?;
128+
129+ if temp. to_uefi ( ) . is_some ( ) { Some ( temp) } else { None }
54130 }
55131
56132 pub fn checked_sub_duration ( & self , other : & Duration ) -> Option < SystemTime > {
57- Some ( SystemTime ( self . 0 . checked_sub ( * other) ?) )
133+ let mut temp = self . clone ( ) ;
134+ temp. dur = self . dur . checked_sub ( * other) ?;
135+
136+ if temp. to_uefi ( ) . is_some ( ) { Some ( temp) } else { None }
58137 }
59138}
60139
@@ -66,25 +145,26 @@ pub(crate) mod system_time_internal {
66145 use crate :: mem:: MaybeUninit ;
67146 use crate :: ptr:: NonNull ;
68147
148+ const SECS_IN_MINUTE : u64 = 60 ;
149+ const SECS_IN_HOUR : u64 = SECS_IN_MINUTE * 60 ;
150+ const SECS_IN_DAY : u64 = SECS_IN_HOUR * 24 ;
151+
69152 pub fn now ( ) -> Option < SystemTime > {
70153 let runtime_services: NonNull < RuntimeServices > = helpers:: runtime_services ( ) ?;
71154 let mut t: MaybeUninit < Time > = MaybeUninit :: uninit ( ) ;
72155 let r = unsafe {
73156 ( ( * runtime_services. as_ptr ( ) ) . get_time ) ( t. as_mut_ptr ( ) , crate :: ptr:: null_mut ( ) )
74157 } ;
75-
76158 if r. is_error ( ) {
77159 return None ;
78160 }
79161
80162 let t = unsafe { t. assume_init ( ) } ;
81163
82- Some ( SystemTime ( uefi_time_to_duration ( t ) ) )
164+ Some ( SystemTime :: from_uefi ( t ) )
83165 }
84166
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 {
167+ pub ( crate ) const fn from_uefi ( t : & Time ) -> Duration {
88168 assert ! ( t. month <= 12 ) ;
89169 assert ! ( t. month != 0 ) ;
90170
@@ -97,7 +177,7 @@ pub(crate) mod system_time_internal {
97177 let y_adj: u32 = ( t. year as u32 ) + YEAR_BASE - carry;
98178 let month_days: u32 = ( m_adj. wrapping_add ( adjust) * 62719 + 769 ) / 2048 ;
99179 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 ;
180+ let days: u32 = y_adj * 365 + leap_days + month_days + ( t. day as u32 - 1 ) - 2447065 ;
101181
102182 let localtime_epoch: u64 = ( days as u64 ) * SECS_IN_DAY
103183 + ( t. second as u64 )
@@ -112,6 +192,55 @@ pub(crate) mod system_time_internal {
112192
113193 Duration :: new ( utc_epoch, t. nanosecond )
114194 }
195+
196+ pub ( crate ) const fn to_uefi ( dur : & Duration , timezone : i16 , daylight : u8 ) -> Option < Time > {
197+ let secs: u64 = if timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
198+ dur. as_secs ( )
199+ } else {
200+ // FIXME: use checked_sub_signed once stablized
201+ dur. as_secs ( ) . checked_add_signed ( ( -timezone as i64 ) * SECS_IN_MINUTE as i64 ) . unwrap ( )
202+ } ;
203+
204+ let days = secs / SECS_IN_DAY ;
205+ let remaining_secs = secs % SECS_IN_DAY ;
206+
207+ let z = days + 693901 ;
208+ let era = z / 146097 ;
209+ let doe = z - ( era * 146097 ) ;
210+ let yoe = ( doe - doe / 1460 + doe / 36524 - doe / 146096 ) / 365 ;
211+ let mut y = yoe + era * 400 ;
212+ let doy = doe - ( 365 * yoe + yoe / 4 - yoe / 100 ) ;
213+ let mp = ( 5 * doy + 2 ) / 153 ;
214+ let d = doy - ( 153 * mp + 2 ) / 5 + 1 ;
215+ let m = if mp < 10 { mp + 3 } else { mp - 9 } ;
216+
217+ if m <= 2 {
218+ y += 1 ;
219+ }
220+
221+ let hour = ( remaining_secs / SECS_IN_HOUR ) as u8 ;
222+ let minute = ( ( remaining_secs % SECS_IN_HOUR ) / SECS_IN_MINUTE ) as u8 ;
223+ let second = ( remaining_secs % SECS_IN_MINUTE ) as u8 ;
224+
225+ // Check Bounds
226+ if y >= 1900 && y <= 9999 {
227+ Some ( Time {
228+ year : y as u16 ,
229+ month : m as u8 ,
230+ day : d as u8 ,
231+ hour,
232+ minute,
233+ second,
234+ nanosecond : dur. subsec_nanos ( ) ,
235+ timezone,
236+ daylight,
237+ pad1 : 0 ,
238+ pad2 : 0 ,
239+ } )
240+ } else {
241+ None
242+ }
243+ }
115244}
116245
117246pub ( crate ) mod instant_internal {
0 commit comments