1
1
use crate :: time:: Duration ;
2
2
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
3
#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
8
4
pub struct Instant ( Duration ) ;
9
5
6
+ /// When a Timezone is specified, the stored Duration is in UTC. If timezone is unspecified, then
7
+ /// the timezone is assumed to be in UTC.
8
+ ///
9
+ /// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00
10
10
#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
11
11
pub struct SystemTime ( Duration ) ;
12
12
13
- pub const UNIX_EPOCH : SystemTime = SystemTime ( Duration :: from_secs ( 0 ) ) ;
13
+ pub const UNIX_EPOCH : SystemTime = SystemTime :: from_uefi ( r_efi:: efi:: Time {
14
+ year : 1970 ,
15
+ month : 1 ,
16
+ day : 1 ,
17
+ hour : 0 ,
18
+ minute : 0 ,
19
+ second : 0 ,
20
+ nanosecond : 0 ,
21
+ timezone : 0 ,
22
+ daylight : 0 ,
23
+ pad1 : 0 ,
24
+ pad2 : 0 ,
25
+ } ) ;
14
26
15
27
impl Instant {
16
28
pub fn now ( ) -> Instant {
@@ -40,6 +52,14 @@ impl Instant {
40
52
}
41
53
42
54
impl SystemTime {
55
+ pub ( crate ) const fn from_uefi ( t : r_efi:: efi:: Time ) -> Self {
56
+ Self ( system_time_internal:: from_uefi ( & t) )
57
+ }
58
+
59
+ pub ( crate ) const fn to_uefi ( self , timezone : i16 , daylight : u8 ) -> Option < r_efi:: efi:: Time > {
60
+ system_time_internal:: to_uefi ( & self . 0 , timezone, daylight)
61
+ }
62
+
43
63
pub fn now ( ) -> SystemTime {
44
64
system_time_internal:: now ( )
45
65
. unwrap_or_else ( || panic ! ( "time not implemented on this platform" ) )
@@ -50,11 +70,17 @@ impl SystemTime {
50
70
}
51
71
52
72
pub fn checked_add_duration ( & self , other : & Duration ) -> Option < SystemTime > {
53
- Some ( SystemTime ( self . 0 . checked_add ( * other) ?) )
73
+ let temp = Self ( self . 0 . checked_add ( * other) ?) ;
74
+
75
+ // Check if can be represented in UEFI
76
+ if temp. to_uefi ( 0 , 0 ) . is_some ( ) { Some ( temp) } else { None }
54
77
}
55
78
56
79
pub fn checked_sub_duration ( & self , other : & Duration ) -> Option < SystemTime > {
57
- Some ( SystemTime ( self . 0 . checked_sub ( * other) ?) )
80
+ let temp = Self ( self . 0 . checked_sub ( * other) ?) ;
81
+
82
+ // Check if can be represented in UEFI
83
+ if temp. to_uefi ( 0 , 0 ) . is_some ( ) { Some ( temp) } else { None }
58
84
}
59
85
}
60
86
@@ -66,25 +92,26 @@ pub(crate) mod system_time_internal {
66
92
use crate :: mem:: MaybeUninit ;
67
93
use crate :: ptr:: NonNull ;
68
94
95
+ const SECS_IN_MINUTE : u64 = 60 ;
96
+ const SECS_IN_HOUR : u64 = SECS_IN_MINUTE * 60 ;
97
+ const SECS_IN_DAY : u64 = SECS_IN_HOUR * 24 ;
98
+
69
99
pub fn now ( ) -> Option < SystemTime > {
70
100
let runtime_services: NonNull < RuntimeServices > = helpers:: runtime_services ( ) ?;
71
101
let mut t: MaybeUninit < Time > = MaybeUninit :: uninit ( ) ;
72
102
let r = unsafe {
73
103
( ( * runtime_services. as_ptr ( ) ) . get_time ) ( t. as_mut_ptr ( ) , crate :: ptr:: null_mut ( ) )
74
104
} ;
75
-
76
105
if r. is_error ( ) {
77
106
return None ;
78
107
}
79
108
80
109
let t = unsafe { t. assume_init ( ) } ;
81
110
82
- Some ( SystemTime ( uefi_time_to_duration ( t ) ) )
111
+ Some ( SystemTime :: from_uefi ( t ) )
83
112
}
84
113
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 {
114
+ pub ( crate ) const fn from_uefi ( t : & Time ) -> Duration {
88
115
assert ! ( t. month <= 12 ) ;
89
116
assert ! ( t. month != 0 ) ;
90
117
@@ -97,7 +124,7 @@ pub(crate) mod system_time_internal {
97
124
let y_adj: u32 = ( t. year as u32 ) + YEAR_BASE - carry;
98
125
let month_days: u32 = ( m_adj. wrapping_add ( adjust) * 62719 + 769 ) / 2048 ;
99
126
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 ;
127
+ let days: u32 = y_adj * 365 + leap_days + month_days + ( t. day as u32 - 1 ) - 2447065 ;
101
128
102
129
let localtime_epoch: u64 = ( days as u64 ) * SECS_IN_DAY
103
130
+ ( t. second as u64 )
@@ -112,6 +139,55 @@ pub(crate) mod system_time_internal {
112
139
113
140
Duration :: new ( utc_epoch, t. nanosecond )
114
141
}
142
+
143
+ pub ( crate ) const fn to_uefi ( dur : & Duration , timezone : i16 , daylight : u8 ) -> Option < Time > {
144
+ let secs: u64 = if timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
145
+ dur. as_secs ( )
146
+ } else {
147
+ // FIXME: use checked_sub_signed once stablized
148
+ dur. as_secs ( ) . checked_add_signed ( ( -timezone as i64 ) * SECS_IN_MINUTE as i64 ) . unwrap ( )
149
+ } ;
150
+
151
+ let days = secs / SECS_IN_DAY ;
152
+ let remaining_secs = secs % SECS_IN_DAY ;
153
+
154
+ let z = days + 693901 ;
155
+ let era = z / 146097 ;
156
+ let doe = z - ( era * 146097 ) ;
157
+ let yoe = ( doe - doe / 1460 + doe / 36524 - doe / 146096 ) / 365 ;
158
+ let mut y = yoe + era * 400 ;
159
+ let doy = doe - ( 365 * yoe + yoe / 4 - yoe / 100 ) ;
160
+ let mp = ( 5 * doy + 2 ) / 153 ;
161
+ let d = doy - ( 153 * mp + 2 ) / 5 + 1 ;
162
+ let m = if mp < 10 { mp + 3 } else { mp - 9 } ;
163
+
164
+ if m <= 2 {
165
+ y += 1 ;
166
+ }
167
+
168
+ let hour = ( remaining_secs / SECS_IN_HOUR ) as u8 ;
169
+ let minute = ( ( remaining_secs % SECS_IN_HOUR ) / SECS_IN_MINUTE ) as u8 ;
170
+ let second = ( remaining_secs % SECS_IN_MINUTE ) as u8 ;
171
+
172
+ // Check Bounds
173
+ if y >= 1900 && y <= 9999 {
174
+ Some ( Time {
175
+ year : y as u16 ,
176
+ month : m as u8 ,
177
+ day : d as u8 ,
178
+ hour,
179
+ minute,
180
+ second,
181
+ nanosecond : dur. subsec_nanos ( ) ,
182
+ timezone,
183
+ daylight,
184
+ pad1 : 0 ,
185
+ pad2 : 0 ,
186
+ } )
187
+ } else {
188
+ None
189
+ }
190
+ }
115
191
}
116
192
117
193
pub ( crate ) mod instant_internal {
0 commit comments