diff options
| author | Ayush Singh <ayush@beagleboard.org> | 2025-05-24 17:06:02 +0530 |
|---|---|---|
| committer | Ayush Singh <ayush@beagleboard.org> | 2025-08-11 19:10:10 +0530 |
| commit | 35906eaabcd8ecf4e92f82630e6fdfbf945f5dc6 (patch) | |
| tree | 6849d48e47b9556a499d7a21cdef6146f638a4a2 /library | |
| parent | 0ccbe8fc71173bf5d2e348e5b3ed6cb344db913b (diff) | |
| download | rust-35906eaabcd8ecf4e92f82630e6fdfbf945f5dc6.tar.gz rust-35906eaabcd8ecf4e92f82630e6fdfbf945f5dc6.zip | |
std: sys: pal: uefi: tests: Add systemtime tests
Add tests to ensure that extream system times are still representable. Signed-off-by: Ayush Singh <ayush@beagleboard.org>
Diffstat (limited to 'library')
| -rw-r--r-- | library/std/src/sys/pal/uefi/tests.rs | 54 | ||||
| -rw-r--r-- | library/std/src/sys/pal/uefi/time.rs | 66 |
2 files changed, 96 insertions, 24 deletions
diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 6a15c03184c..56ca999cc7e 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -2,10 +2,12 @@ //! to a separate project when modifying any related code. use super::alloc::*; -use super::time::*; +use super::time::system_time_internal::{from_uefi, to_uefi}; use crate::io::{IoSlice, IoSliceMut}; use crate::time::Duration; +const SECS_IN_MINUTE: u64 = 60; + #[test] fn align() { // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be @@ -27,21 +29,61 @@ fn align() { } #[test] -fn epoch() { - let t = r_efi::system::Time { - year: 1970, +fn systemtime_start() { + let t = r_efi::efi::Time { + year: 1900, month: 1, day: 1, hour: 0, minute: 0, second: 0, nanosecond: 0, - timezone: r_efi::efi::UNSPECIFIED_TIMEZONE, + timezone: -1440, daylight: 0, + pad2: 0, + }; + assert_eq!(from_uefi(&t), Duration::new(0, 0)); + assert_eq!(t, to_uefi(&from_uefi(&t), -1440, 0).unwrap()); + assert!(to_uefi(&from_uefi(&t), 0, 0).is_none()); +} + +#[test] +fn systemtime_utc_start() { + let t = r_efi::efi::Time { + year: 1900, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, pad1: 0, + nanosecond: 0, + timezone: 0, + daylight: 0, + pad2: 0, + }; + assert_eq!(from_uefi(&t), Duration::new(1440 * SECS_IN_MINUTE, 0)); + assert_eq!(t, to_uefi(&from_uefi(&t), 0, 0).unwrap()); + assert!(to_uefi(&from_uefi(&t), -1440, 0).is_some()); +} + +#[test] +fn systemtime_end() { + let t = r_efi::efi::Time { + year: 9999, + month: 12, + day: 31, + hour: 23, + minute: 59, + second: 59, + pad1: 0, + nanosecond: 0, + timezone: 1440, + daylight: 0, pad2: 0, }; - assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0)); + assert!(to_uefi(&from_uefi(&t), 1440, 0).is_some()); + assert!(to_uefi(&from_uefi(&t), 1439, 0).is_none()); } // UEFI IoSlice and IoSliceMut Tests diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index 32c3cfd143c..a5ab7690327 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -6,7 +6,7 @@ pub struct Instant(Duration); /// When a Timezone is specified, the stored Duration is in UTC. If timezone is unspecified, then /// the timezone is assumed to be in UTC. /// -/// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00 +/// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00 with timezone -1440 as anchor #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct SystemTime(Duration); @@ -24,6 +24,20 @@ pub const UNIX_EPOCH: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { pad2: 0, }); +const MAX_UEFI_TIME: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { + year: 9999, + month: 12, + day: 31, + hour: 23, + minute: 59, + second: 59, + nanosecond: 999_999_999, + timezone: 1440, + daylight: 0, + pad1: 0, + pad2: 0, +}); + impl Instant { pub fn now() -> Instant { // If we have a timestamp protocol, use it. @@ -56,6 +70,7 @@ impl SystemTime { Self(system_time_internal::from_uefi(&t)) } + #[expect(dead_code)] pub(crate) const fn to_uefi(self, timezone: i16, daylight: u8) -> Option<r_efi::efi::Time> { system_time_internal::to_uefi(&self.0, timezone, daylight) } @@ -73,14 +88,11 @@ impl SystemTime { let temp = Self(self.0.checked_add(*other)?); // Check if can be represented in UEFI - if temp.to_uefi(0, 0).is_some() { Some(temp) } else { None } + if temp <= MAX_UEFI_TIME { Some(temp) } else { None } } pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { - let temp = Self(self.0.checked_sub(*other)?); - - // Check if can be represented in UEFI - if temp.to_uefi(0, 0).is_some() { Some(temp) } else { None } + self.0.checked_sub(*other).map(Self) } } @@ -112,13 +124,30 @@ pub(crate) mod system_time_internal { Some(SystemTime::from_uefi(t)) } + /// This algorithm is a modified form of the one described in the post + /// https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html + /// + /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX + /// epoch used in the original algorithm. pub(crate) const fn from_uefi(t: &Time) -> Duration { - assert!(t.month <= 12); - assert!(t.month != 0); + assert!(t.month <= 12 && t.month != 0); + assert!(t.year >= 1900 && t.year <= 9999); + assert!(t.day <= 31 && t.day != 0); + + assert!(t.second < 60); + assert!(t.minute < 60); + assert!(t.hour < 24); + assert!(t.nanosecond < 1_000_000_000); + + assert!( + (t.timezone <= 1440 && t.timezone >= -1440) + || t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE + ); const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */ - // Calculate the number of days since 1/1/1900 + // Calculate the number of days since 1/1/1900. This is the earliest supported date in UEFI + // time. // Use 1 March as the start let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3); let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) }; @@ -146,19 +175,20 @@ pub(crate) mod system_time_internal { Duration::new(epoch, t.nanosecond) } + /// This algorithm is a modifed version of the one described in the post: + /// https://howardhinnant.github.io/date_algorithms.html#clive_from_days + /// + /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX + /// epoch used in the original algorithm. pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Option<Time> { // Check timzone validity - assert!(timezone <= 1440); - assert!(timezone >= -1440); + assert!(timezone <= 1440 && timezone >= -1440); - let secs: u64 = if timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { - dur.as_secs() - } else { - // FIXME: use checked_sub_signed once stablized - dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap() - }; + // FIXME(#126043): use checked_sub_signed once stablized + let secs = + dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap(); - // Convert to UTC + // Convert to seconds since 1900-01-01-00:00:00 in timezone. let Some(secs) = secs.checked_sub(TIMEZONE_DELTA) else { return None }; let days = secs / SECS_IN_DAY; |
