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 ) ]
8
+ pub struct SystemTime {
9
+ // Duration from 1900-01-01-00:00:00
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
+ if self . timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
56
+ self . timezone . hash ( state) ;
57
+ }
58
+ }
59
+ }
12
60
13
- pub const UNIX_EPOCH : SystemTime = SystemTime ( Duration :: from_secs ( 0 ) ) ;
61
+ pub const UNIX_EPOCH : SystemTime = SystemTime :: from_uefi ( r_efi:: efi:: Time {
62
+ year : 1970 ,
63
+ month : 1 ,
64
+ day : 1 ,
65
+ hour : 0 ,
66
+ minute : 0 ,
67
+ second : 0 ,
68
+ nanosecond : 0 ,
69
+ timezone : 0 ,
70
+ daylight : 0 ,
71
+ pad1 : 0 ,
72
+ pad2 : 0 ,
73
+ } ) ;
14
74
15
75
impl Instant {
16
76
pub fn now ( ) -> Instant {
@@ -40,21 +100,46 @@ impl Instant {
40
100
}
41
101
42
102
impl SystemTime {
103
+ pub ( crate ) const fn from_uefi ( t : r_efi:: efi:: Time ) -> Self {
104
+ let dur = system_time_internal:: from_uefi ( & t) ;
105
+ Self { dur, timezone : t. timezone , daylight : t. daylight }
106
+ }
107
+
108
+ pub ( crate ) const fn to_uefi ( self ) -> Option < r_efi:: efi:: Time > {
109
+ system_time_internal:: to_uefi ( & self . dur , self . timezone , self . daylight )
110
+ }
111
+
43
112
pub fn now ( ) -> SystemTime {
44
113
system_time_internal:: now ( )
45
114
. unwrap_or_else ( || panic ! ( "time not implemented on this platform" ) )
46
115
}
47
116
48
117
pub fn sub_time ( & self , other : & SystemTime ) -> Result < Duration , Duration > {
49
- self . 0 . checked_sub ( other. 0 ) . ok_or_else ( || other. 0 - self . 0 )
118
+ // Comparison between unspecified timezone and normal timezone is not allowed
119
+ if ( self . timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE
120
+ || other. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE )
121
+ && ( self . timezone != other. timezone )
122
+ {
123
+ panic ! ( "Cannot compare unspecified timezone with a normal timezone" )
124
+ } else {
125
+ self . dur . checked_sub ( other. dur ) . ok_or_else ( || other. dur - self . dur )
126
+ }
50
127
}
51
128
52
129
pub fn checked_add_duration ( & self , other : & Duration ) -> Option < SystemTime > {
53
- Some ( SystemTime ( self . 0 . checked_add ( * other) ?) )
130
+ let mut temp = self . clone ( ) ;
131
+ temp. dur = self . dur . checked_add ( * other) ?;
132
+
133
+ // Check if can be represented in UEFI
134
+ if temp. to_uefi ( ) . is_some ( ) { Some ( temp) } else { None }
54
135
}
55
136
56
137
pub fn checked_sub_duration ( & self , other : & Duration ) -> Option < SystemTime > {
57
- Some ( SystemTime ( self . 0 . checked_sub ( * other) ?) )
138
+ let mut temp = self . clone ( ) ;
139
+ temp. dur = self . dur . checked_sub ( * other) ?;
140
+
141
+ // Check if can be represented in UEFI
142
+ if temp. to_uefi ( ) . is_some ( ) { Some ( temp) } else { None }
58
143
}
59
144
}
60
145
@@ -66,25 +151,26 @@ pub(crate) mod system_time_internal {
66
151
use crate :: mem:: MaybeUninit ;
67
152
use crate :: ptr:: NonNull ;
68
153
154
+ const SECS_IN_MINUTE : u64 = 60 ;
155
+ const SECS_IN_HOUR : u64 = SECS_IN_MINUTE * 60 ;
156
+ const SECS_IN_DAY : u64 = SECS_IN_HOUR * 24 ;
157
+
69
158
pub fn now ( ) -> Option < SystemTime > {
70
159
let runtime_services: NonNull < RuntimeServices > = helpers:: runtime_services ( ) ?;
71
160
let mut t: MaybeUninit < Time > = MaybeUninit :: uninit ( ) ;
72
161
let r = unsafe {
73
162
( ( * runtime_services. as_ptr ( ) ) . get_time ) ( t. as_mut_ptr ( ) , crate :: ptr:: null_mut ( ) )
74
163
} ;
75
-
76
164
if r. is_error ( ) {
77
165
return None ;
78
166
}
79
167
80
168
let t = unsafe { t. assume_init ( ) } ;
81
169
82
- Some ( SystemTime ( uefi_time_to_duration ( t ) ) )
170
+ Some ( SystemTime :: from_uefi ( t ) )
83
171
}
84
172
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 {
173
+ pub ( crate ) const fn from_uefi ( t : & Time ) -> Duration {
88
174
assert ! ( t. month <= 12 ) ;
89
175
assert ! ( t. month != 0 ) ;
90
176
@@ -97,7 +183,7 @@ pub(crate) mod system_time_internal {
97
183
let y_adj: u32 = ( t. year as u32 ) + YEAR_BASE - carry;
98
184
let month_days: u32 = ( m_adj. wrapping_add ( adjust) * 62719 + 769 ) / 2048 ;
99
185
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 ;
186
+ let days: u32 = y_adj * 365 + leap_days + month_days + ( t. day as u32 - 1 ) - 2447065 ;
101
187
102
188
let localtime_epoch: u64 = ( days as u64 ) * SECS_IN_DAY
103
189
+ ( t. second as u64 )
@@ -112,6 +198,55 @@ pub(crate) mod system_time_internal {
112
198
113
199
Duration :: new ( utc_epoch, t. nanosecond )
114
200
}
201
+
202
+ pub ( crate ) const fn to_uefi ( dur : & Duration , timezone : i16 , daylight : u8 ) -> Option < Time > {
203
+ let secs: u64 = if timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
204
+ dur. as_secs ( )
205
+ } else {
206
+ // FIXME: use checked_sub_signed once stablized
207
+ dur. as_secs ( ) . checked_add_signed ( ( -timezone as i64 ) * SECS_IN_MINUTE as i64 ) . unwrap ( )
208
+ } ;
209
+
210
+ let days = secs / SECS_IN_DAY ;
211
+ let remaining_secs = secs % SECS_IN_DAY ;
212
+
213
+ let z = days + 693901 ;
214
+ let era = z / 146097 ;
215
+ let doe = z - ( era * 146097 ) ;
216
+ let yoe = ( doe - doe / 1460 + doe / 36524 - doe / 146096 ) / 365 ;
217
+ let mut y = yoe + era * 400 ;
218
+ let doy = doe - ( 365 * yoe + yoe / 4 - yoe / 100 ) ;
219
+ let mp = ( 5 * doy + 2 ) / 153 ;
220
+ let d = doy - ( 153 * mp + 2 ) / 5 + 1 ;
221
+ let m = if mp < 10 { mp + 3 } else { mp - 9 } ;
222
+
223
+ if m <= 2 {
224
+ y += 1 ;
225
+ }
226
+
227
+ let hour = ( remaining_secs / SECS_IN_HOUR ) as u8 ;
228
+ let minute = ( ( remaining_secs % SECS_IN_HOUR ) / SECS_IN_MINUTE ) as u8 ;
229
+ let second = ( remaining_secs % SECS_IN_MINUTE ) as u8 ;
230
+
231
+ // Check Bounds
232
+ if y >= 1900 && y <= 9999 {
233
+ Some ( Time {
234
+ year : y as u16 ,
235
+ month : m as u8 ,
236
+ day : d as u8 ,
237
+ hour,
238
+ minute,
239
+ second,
240
+ nanosecond : dur. subsec_nanos ( ) ,
241
+ timezone,
242
+ daylight,
243
+ pad1 : 0 ,
244
+ pad2 : 0 ,
245
+ } )
246
+ } else {
247
+ None
248
+ }
249
+ }
115
250
}
116
251
117
252
pub ( crate ) mod instant_internal {
0 commit comments