Skip to content

Commit f4cd07a

Browse files
committed
std: sys: pal: uefi: Overhaul Time
Use UEFI time format for SystemTime. Cannot use the UEFI struct directly since it does not implement proper traits. All calculations and comparisons are being done using UnixTime since UEFI Time format does not seem good fit for those calculations. Signed-off-by: Ayush Singh <[email protected]>
1 parent 092a284 commit f4cd07a

File tree

2 files changed

+258
-62
lines changed

2 files changed

+258
-62
lines changed

library/std/src/sys/pal/uefi/tests.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
use super::alloc::*;
2-
use super::time::*;
3-
use crate::time::Duration;
42

53
#[test]
64
fn align() {
@@ -21,21 +19,3 @@ fn align() {
2119
}
2220
}
2321
}
24-
25-
#[test]
26-
fn epoch() {
27-
let t = r_efi::system::Time {
28-
year: 1970,
29-
month: 1,
30-
day: 1,
31-
hour: 0,
32-
minute: 0,
33-
second: 0,
34-
nanosecond: 0,
35-
timezone: r_efi::efi::UNSPECIFIED_TIMEZONE,
36-
daylight: 0,
37-
pad1: 0,
38-
pad2: 0,
39-
};
40-
assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0));
41-
}

library/std/src/sys/pal/uefi/time.rs

Lines changed: 258 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,55 @@
1+
use crate::cmp::Ordering;
12
use 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)]
85
pub 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

1554
impl Instant {
1655
pub fn now() -> Instant {
@@ -40,21 +79,61 @@ impl Instant {
4079
}
4180

4281
impl 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

Comments
 (0)