diff options
| author | bors <bors@rust-lang.org> | 2025-08-12 12:37:26 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-08-12 12:37:26 +0000 |
| commit | d9dba3a55476ae2da5d4e5bce8a81b341c675750 (patch) | |
| tree | 56fcfbf6c3dda250b2b2b8edb14cdac179edc413 /library/std/src | |
| parent | a1531335fe2807715fff569904d99602022643a7 (diff) | |
| parent | 7c4bedc962ac1800877899dd5bae9ca472e57a87 (diff) | |
| download | rust-d9dba3a55476ae2da5d4e5bce8a81b341c675750.tar.gz rust-d9dba3a55476ae2da5d4e5bce8a81b341c675750.zip | |
Auto merge of #145300 - Zalathar:rollup-0eqbt6a, r=Zalathar
Rollup of 17 pull requests Successful merges: - rust-lang/rust#131477 (Apple: Always pass SDK root when linking with `cc`, and pass it via `SDKROOT` env var) - rust-lang/rust#139806 (std: sys: pal: uefi: Overhaul Time) - rust-lang/rust#144386 (Extract TraitImplHeader in AST/HIR) - rust-lang/rust#144921 (Don't emit `rustdoc::broken_intra_doc_links` for GitHub-flavored Markdown admonitions like `[!NOTE]`) - rust-lang/rust#145155 (Port `#[allow_internal_unsafe]` to the new attribute system (attempt 2)) - rust-lang/rust#145214 (fix: re-enable self-assignment) - rust-lang/rust#145216 (rustdoc: correct negative-to-implicit discriminant display) - rust-lang/rust#145238 (Tweak invalid builtin attribute output) - rust-lang/rust#145249 (Rename entered trace span variables from `_span` to `_trace`) - rust-lang/rust#145251 (Support using #[unstable_feature_bound] on trait) - rust-lang/rust#145253 (Document compiler and stdlib in stage1 in `pr-check-2` CI job) - rust-lang/rust#145260 (Make explicit guarantees about `Vec`’s allocator) - rust-lang/rust#145263 (Update books) - rust-lang/rust#145273 (Account for new `assert!` desugaring in `!condition` suggestion) - rust-lang/rust#145283 (Make I-miscompile imply I-prioritize) - rust-lang/rust#145291 (bootstrap: Only warn about `rust.debug-assertions` if downloading rustc) - rust-lang/rust#145292 (Fix a typo in range docs) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/sys/pal/uefi/tests.rs | 57 | ||||
| -rw-r--r-- | library/std/src/sys/pal/uefi/time.rs | 159 |
2 files changed, 190 insertions, 26 deletions
diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 49e75a1a70d..56ca999cc7e 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,8 +1,13 @@ +//! These tests are not run automatically right now. Please run these tests manually by copying them +//! 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 @@ -24,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: -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: r_efi::efi::UNSPECIFIED_TIMEZONE, + 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 eeb2c35ffbb..a5ab7690327 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -1,16 +1,42 @@ use crate::time::Duration; -const SECS_IN_MINUTE: u64 = 60; -const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; -const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] 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 with timezone -1440 as anchor #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct SystemTime(Duration); -pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); +pub const UNIX_EPOCH: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + nanosecond: 0, + timezone: 0, + daylight: 0, + pad1: 0, + 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 { @@ -40,6 +66,15 @@ impl Instant { } impl SystemTime { + pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self { + 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) + } + pub fn now() -> SystemTime { system_time_internal::now() .unwrap_or_else(|| panic!("time not implemented on this platform")) @@ -50,11 +85,14 @@ impl SystemTime { } pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime(self.0.checked_add(*other)?)) + let temp = Self(self.0.checked_add(*other)?); + + // Check if can be represented in UEFI + if temp <= MAX_UEFI_TIME { Some(temp) } else { None } } pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime(self.0.checked_sub(*other)?)) + self.0.checked_sub(*other).map(Self) } } @@ -66,51 +104,132 @@ pub(crate) mod system_time_internal { use crate::mem::MaybeUninit; use crate::ptr::NonNull; + const SECS_IN_MINUTE: u64 = 60; + const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; + const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; + const TIMEZONE_DELTA: u64 = 1440 * SECS_IN_MINUTE; + pub fn now() -> Option<SystemTime> { let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?; let mut t: MaybeUninit<Time> = MaybeUninit::uninit(); let r = unsafe { ((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut()) }; - if r.is_error() { return None; } let t = unsafe { t.assume_init() }; - Some(SystemTime(uefi_time_to_duration(t))) + Some(SystemTime::from_uefi(t)) } - // This algorithm is based on the one described in the post - // https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html - pub(crate) const fn uefi_time_to_duration(t: r_efi::system::Time) -> Duration { - assert!(t.month <= 12); - assert!(t.month != 0); + /// 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 && 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/1970 + // 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) }; let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry; let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048; let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400; - let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2472632; + let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065; let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY + (t.second as u64) + (t.minute as u64) * SECS_IN_MINUTE + (t.hour as u64) * SECS_IN_HOUR; - let utc_epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { - localtime_epoch + // Calculate the offset from 1/1/1900 at timezone -1440 min + let adjusted_localtime_epoc: u64 = localtime_epoch + TIMEZONE_DELTA; + + let epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { + adjusted_localtime_epoc } else { - (localtime_epoch as i64 + (t.timezone as i64) * SECS_IN_MINUTE as i64) as u64 + adjusted_localtime_epoc + .checked_add_signed((t.timezone as i64) * SECS_IN_MINUTE as i64) + .unwrap() }; - Duration::new(utc_epoch, t.nanosecond) + 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 && timezone >= -1440); + + // 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 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; + let remaining_secs = secs % SECS_IN_DAY; + + let z = days + 693901; + let era = z / 146097; + let doe = z - (era * 146097); + let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + let mut y = yoe + era * 400; + let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + let mp = (5 * doy + 2) / 153; + let d = doy - (153 * mp + 2) / 5 + 1; + let m = if mp < 10 { mp + 3 } else { mp - 9 }; + + if m <= 2 { + y += 1; + } + + let hour = (remaining_secs / SECS_IN_HOUR) as u8; + let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8; + let second = (remaining_secs % SECS_IN_MINUTE) as u8; + + // Check Bounds + if y >= 1900 && y <= 9999 { + Some(Time { + year: y as u16, + month: m as u8, + day: d as u8, + hour, + minute, + second, + nanosecond: dur.subsec_nanos(), + timezone, + daylight, + pad1: 0, + pad2: 0, + }) + } else { + None + } } } |
