Skip to content

Commit 51e4cb2

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 51e4cb2

File tree

2 files changed

+89
-33
lines changed

2 files changed

+89
-33
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: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
use crate::time::Duration;
22

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-
73
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
84
pub struct Instant(Duration);
95

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
1010
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1111
pub struct SystemTime(Duration);
1212

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+
});
1426

1527
impl Instant {
1628
pub fn now() -> Instant {
@@ -40,6 +52,14 @@ impl Instant {
4052
}
4153

4254
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+
4363
pub fn now() -> SystemTime {
4464
system_time_internal::now()
4565
.unwrap_or_else(|| panic!("time not implemented on this platform"))
@@ -50,11 +70,17 @@ impl SystemTime {
5070
}
5171

5272
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 }
5477
}
5578

5679
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 }
5884
}
5985
}
6086

@@ -66,25 +92,26 @@ pub(crate) mod system_time_internal {
6692
use crate::mem::MaybeUninit;
6793
use crate::ptr::NonNull;
6894

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+
6999
pub fn now() -> Option<SystemTime> {
70100
let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?;
71101
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
72102
let r = unsafe {
73103
((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut())
74104
};
75-
76105
if r.is_error() {
77106
return None;
78107
}
79108

80109
let t = unsafe { t.assume_init() };
81110

82-
Some(SystemTime(uefi_time_to_duration(t)))
111+
Some(SystemTime::from_uefi(t))
83112
}
84113

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 {
88115
assert!(t.month <= 12);
89116
assert!(t.month != 0);
90117

@@ -97,7 +124,7 @@ pub(crate) mod system_time_internal {
97124
let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
98125
let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
99126
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;
101128

102129
let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
103130
+ (t.second as u64)
@@ -112,6 +139,55 @@ pub(crate) mod system_time_internal {
112139

113140
Duration::new(utc_epoch, t.nanosecond)
114141
}
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+
}
115191
}
116192

117193
pub(crate) mod instant_internal {

0 commit comments

Comments
 (0)