Skip to content

Commit 36dd216

Browse files
committed
std: sys: pal: uefi: Overhaul Time
Use a time representation with 1900-01-01-00:00:00 as anchor. This is the earliest time supported in UEFI. Signed-off-by: Ayush Singh <[email protected]>
1 parent 092a284 commit 36dd216

File tree

2 files changed

+148
-36
lines changed

2 files changed

+148
-36
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: 148 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,73 @@
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)]
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+
}
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

1572
impl Instant {
1673
pub fn now() -> Instant {
@@ -40,21 +97,46 @@ impl Instant {
4097
}
4198

4299
impl 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+
// Comparison between unspecified timezone and normal timezone is not allowed
116+
if (self.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE
117+
|| other.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE)
118+
&& (self.timezone != other.timezone)
119+
{
120+
panic!("Cannot compare unspecified timezone with a normal timezone")
121+
} else {
122+
self.dur.checked_sub(other.dur).ok_or_else(|| other.dur - self.dur)
123+
}
50124
}
51125

52126
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
53-
Some(SystemTime(self.0.checked_add(*other)?))
127+
let mut temp = self.clone();
128+
temp.dur = self.dur.checked_add(*other)?;
129+
130+
// Check if can be represented in UEFI
131+
if temp.to_uefi().is_some() { Some(temp) } else { None }
54132
}
55133

56134
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
57-
Some(SystemTime(self.0.checked_sub(*other)?))
135+
let mut temp = self.clone();
136+
temp.dur = self.dur.checked_sub(*other)?;
137+
138+
// Check if can be represented in UEFI
139+
if temp.to_uefi().is_some() { Some(temp) } else { None }
58140
}
59141
}
60142

@@ -66,25 +148,26 @@ pub(crate) mod system_time_internal {
66148
use crate::mem::MaybeUninit;
67149
use crate::ptr::NonNull;
68150

151+
const SECS_IN_MINUTE: u64 = 60;
152+
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
153+
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
154+
69155
pub fn now() -> Option<SystemTime> {
70156
let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?;
71157
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
72158
let r = unsafe {
73159
((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut())
74160
};
75-
76161
if r.is_error() {
77162
return None;
78163
}
79164

80165
let t = unsafe { t.assume_init() };
81166

82-
Some(SystemTime(uefi_time_to_duration(t)))
167+
Some(SystemTime::from_uefi(t))
83168
}
84169

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 {
170+
pub(crate) const fn from_uefi(t: &Time) -> Duration {
88171
assert!(t.month <= 12);
89172
assert!(t.month != 0);
90173

@@ -97,7 +180,7 @@ pub(crate) mod system_time_internal {
97180
let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
98181
let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
99182
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;
183+
let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065;
101184

102185
let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
103186
+ (t.second as u64)
@@ -112,6 +195,55 @@ pub(crate) mod system_time_internal {
112195

113196
Duration::new(utc_epoch, t.nanosecond)
114197
}
198+
199+
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Option<Time> {
200+
let secs: u64 = if timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
201+
dur.as_secs()
202+
} else {
203+
// FIXME: use checked_sub_signed once stablized
204+
dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap()
205+
};
206+
207+
let days = secs / SECS_IN_DAY;
208+
let remaining_secs = secs % SECS_IN_DAY;
209+
210+
let z = days + 693901;
211+
let era = z / 146097;
212+
let doe = z - (era * 146097);
213+
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
214+
let mut y = yoe + era * 400;
215+
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
216+
let mp = (5 * doy + 2) / 153;
217+
let d = doy - (153 * mp + 2) / 5 + 1;
218+
let m = if mp < 10 { mp + 3 } else { mp - 9 };
219+
220+
if m <= 2 {
221+
y += 1;
222+
}
223+
224+
let hour = (remaining_secs / SECS_IN_HOUR) as u8;
225+
let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
226+
let second = (remaining_secs % SECS_IN_MINUTE) as u8;
227+
228+
// Check Bounds
229+
if y >= 1900 && y <= 9999 {
230+
Some(Time {
231+
year: y as u16,
232+
month: m as u8,
233+
day: d as u8,
234+
hour,
235+
minute,
236+
second,
237+
nanosecond: dur.subsec_nanos(),
238+
timezone,
239+
daylight,
240+
pad1: 0,
241+
pad2: 0,
242+
})
243+
} else {
244+
None
245+
}
246+
}
115247
}
116248

117249
pub(crate) mod instant_internal {

0 commit comments

Comments
 (0)