Skip to content

Commit c383477

Browse files
committed
uefi: add time03 feature and integrate time types with struct Time
1 parent 6681ed3 commit c383477

9 files changed

Lines changed: 189 additions & 6 deletions

File tree

Cargo.lock

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ rust-version = "1.88"
2323
bitflags = "2.0.0"
2424
log = { version = "0.4.5", default-features = false }
2525
ptr_meta = { version = "0.3.0", default-features = false, features = ["derive"] }
26+
time = { version = "0.3", default-features = false }
2627
uguid = "2.2.1"
2728

2829
[patch.crates-io]

uefi/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
- Added `Handle::component_name()` and `Handle::device_path()` to simplify the
2020
common use-case of querying more information about a handle.
2121
- Added `fs::path::Path::join()`.
22+
- Integration of `Time` with `time` crate
23+
- `TryFrom`: `time::PrimitiveDateTime <--> Time` (without timezone)
24+
- `TryFrom`: `time::OffsetDateTime <--> Time` (with timezone)
2225

2326
## Changed
2427
- export all `text::{input, output}::*` types

uefi/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ rust-version.workspace = true
2020
# KEEP this feature list in sync with doc in uefi/lib.rs!
2121
default = [ ]
2222
alloc = []
23+
# Integration with time crate `>= v0.3`
24+
time03 = ["dep:time"]
2325

2426
# Generic gate to code that uses unstable features of Rust, needing a nightly
2527
# toolchain.
@@ -39,6 +41,7 @@ log-debugcon = []
3941
bitflags.workspace = true
4042
log.workspace = true
4143
ptr_meta.workspace = true
44+
time = { workspace = true, optional = true }
4245
uguid.workspace = true
4346
cfg-if = "1.0.0"
4447
ucs2 = "0.3.3"

uefi/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@
148148
//! - `log-debugcon`: Whether the logger set up by `logger` should also log
149149
//! to the debugcon device (available in QEMU or Cloud Hypervisor on x86).
150150
//! - `panic_handler`: Add a default panic handler that logs to `stdout`.
151+
//! - `time03`: Integration of [`runtime::Time`] with the `time` crate
152+
//! (version 0.3 and possible above). Specifically, it integrates the time
153+
//! struct with `PrimitiveDateTime` and `OffsetDateTime` via `TryFrom`.
151154
//! - `unstable`: Enable functionality that depends on [unstable features] in
152155
//! the Rust compiler (nightly version).
153156
//! - `qemu`: Enable some code paths to adapt their execution when executed

uefi/src/runtime/time_defs/integration_common.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ pub(super) enum ConversionErrorInner {
3838
///
3939
/// [`Time::UNSPECIFIED_TIMEZONE`]: super::Time::UNSPECIFIED_TIMEZONE
4040
UnspecifiedTimezone,
41+
/// Errors raised in the [`time`] crate.
42+
#[cfg(feature = "time03")]
43+
TimeCrateError(time::Error),
4144
}
4245

4346
impl Display for ConversionErrorInner {
@@ -46,6 +49,8 @@ impl Display for ConversionErrorInner {
4649
Self::InvalidComponent => write!(f, "Invalid component"),
4750
Self::InvalidUefiTime(e) => write!(f, "Invalid UEFI time: {e}"),
4851
Self::UnspecifiedTimezone => write!(f, "Unspecified timezone"),
52+
#[cfg(feature = "time03")]
53+
Self::TimeCrateError(e) => write!(f, "Time crate error: {}", e),
4954
}
5055
}
5156
}
@@ -56,6 +61,8 @@ impl Error for ConversionErrorInner {
5661
Self::InvalidComponent => None,
5762
Self::InvalidUefiTime(e) => Some(e),
5863
Self::UnspecifiedTimezone => None,
64+
#[cfg(feature = "time03")]
65+
Self::TimeCrateError(e) => Some(e),
5966
}
6067
}
6168
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Integration of the UEFI [`Time`] type with the [`time`] crate.
4+
5+
use super::Time;
6+
use super::integration_common::{ConversionError, ConversionErrorInner};
7+
use crate::runtime::TimeParams;
8+
use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
9+
10+
impl TryFrom<Time> for PrimitiveDateTime {
11+
type Error = ConversionError;
12+
13+
fn try_from(value: Time) -> Result<Self, Self::Error> {
14+
if let Err(e) = value.is_valid() {
15+
return Err(ConversionError(ConversionErrorInner::InvalidUefiTime(e)));
16+
}
17+
18+
// Emulated try {} block to keep the `?` error propagation scoped
19+
// (we have a different error type here)
20+
let datetime: Result<Self, time::error::ComponentRange> = (|| {
21+
let month = time::Month::try_from(value.0.month)?;
22+
let date = time::Date::from_calendar_date(value.0.year as i32, month, value.0.day)?;
23+
let time = time::Time::from_hms_nano(
24+
value.0.hour,
25+
value.0.minute,
26+
value.0.second,
27+
value.0.nanosecond,
28+
)?;
29+
Ok(Self::new(date, time))
30+
})();
31+
32+
datetime
33+
.map_err(time::Error::ComponentRange)
34+
.map_err(|e| ConversionError(ConversionErrorInner::TimeCrateError(e)))
35+
}
36+
}
37+
38+
impl TryFrom<Time> for OffsetDateTime {
39+
type Error = ConversionError;
40+
41+
fn try_from(value: Time) -> Result<Self, Self::Error> {
42+
if let Err(e) = value.is_valid() {
43+
return Err(ConversionError(ConversionErrorInner::InvalidUefiTime(e)));
44+
}
45+
46+
let primitive_date_time: PrimitiveDateTime = value.try_into()?;
47+
48+
if value.0.time_zone == Time::UNSPECIFIED_TIMEZONE {
49+
return Err(ConversionError(ConversionErrorInner::UnspecifiedTimezone));
50+
}
51+
52+
let h = (value.0.time_zone / 60) as i8;
53+
let m = (value.0.time_zone.abs() % 60) as i8;
54+
55+
let offset = UtcOffset::from_hms(h, m, 0)
56+
.map_err(time::Error::ComponentRange)
57+
.map_err(|e| ConversionError(ConversionErrorInner::TimeCrateError(e)))?;
58+
Ok(primitive_date_time.assume_offset(offset))
59+
}
60+
}
61+
62+
impl TryFrom<PrimitiveDateTime> for Time {
63+
type Error = ConversionError;
64+
65+
fn try_from(value: PrimitiveDateTime) -> Result<Self, Self::Error> {
66+
let params = TimeParams {
67+
year: u16::try_from(value.year())
68+
.map_err(|_e| ConversionError(ConversionErrorInner::InvalidComponent))?,
69+
month: u8::from(value.month()),
70+
day: value.day(),
71+
hour: value.hour(),
72+
minute: value.minute(),
73+
second: value.second(),
74+
nanosecond: value.nanosecond(),
75+
time_zone: None,
76+
daylight: Default::default(),
77+
};
78+
Self::new(params).map_err(|e| ConversionError(ConversionErrorInner::InvalidUefiTime(e)))
79+
}
80+
}
81+
82+
impl TryFrom<OffsetDateTime> for Time {
83+
type Error = ConversionError;
84+
85+
fn try_from(value: OffsetDateTime) -> Result<Self, Self::Error> {
86+
let timezone_offset_minutes = value.offset().whole_seconds() / 60;
87+
if value.offset().whole_seconds() % 60 != 0 {
88+
return Err(ConversionError(ConversionErrorInner::InvalidComponent));
89+
}
90+
91+
let params = TimeParams {
92+
year: u16::try_from(value.year())
93+
.map_err(|_e| ConversionError(ConversionErrorInner::InvalidComponent))?,
94+
month: u8::from(value.month()),
95+
day: value.day(),
96+
hour: value.hour(),
97+
minute: value.minute(),
98+
second: value.second(),
99+
nanosecond: value.nanosecond(),
100+
time_zone: Some(
101+
i16::try_from(timezone_offset_minutes)
102+
.map_err(|_e| ConversionError(ConversionErrorInner::InvalidComponent))?,
103+
),
104+
daylight: Default::default(),
105+
};
106+
107+
Self::new(params).map_err(|e| ConversionError(ConversionErrorInner::InvalidUefiTime(e)))
108+
}
109+
}

uefi/src/runtime/time_defs/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,25 @@ use core::fmt;
77
use core::fmt::{Debug, Display, Formatter};
88
use uefi_raw::time::Daylight;
99

10-
#[allow(unused)]
10+
#[cfg(feature = "time03")]
1111
mod integration_common;
12+
#[cfg(feature = "time03")]
13+
mod integration_time_crate;
1214

1315
/// Date and time representation.
16+
///
17+
/// # Integration with Time-related Crates of the Ecosystem
18+
///
19+
/// Handling time is complicated. Therefore, we do not reinvent the wheel and
20+
/// forward all complexity of time to well-known crates of the ecosystem. For
21+
/// that, we provide integrations with various crates:
22+
///
23+
/// ## Integration with [`time`][time crate] crate
24+
///
25+
/// - [`TryFrom`]: `PrimitiveDateTime` <--> [`Time`] (without timezone)
26+
/// - [`TryFrom`]: `OffsetDateTime` <--> [`Time`] (with timezone)
27+
///
28+
/// [time crate]: https://crates.io/crates/time
1429
#[derive(Copy, Clone, Eq, PartialEq)]
1530
#[repr(transparent)]
1631
pub struct Time(uefi_raw::time::Time);

xtask/src/cargo.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub enum Feature {
5656
Unstable,
5757
PanicHandler,
5858
Qemu,
59+
Time03,
5960

6061
// `uefi-test-runner` features.
6162
DebugSupport,
@@ -76,6 +77,7 @@ impl Feature {
7677
Self::Unstable => "unstable",
7778
Self::PanicHandler => "panic_handler",
7879
Self::Qemu => "qemu",
80+
Self::Time03 => "time03",
7981

8082
Self::DebugSupport => "uefi-test-runner/debug_support",
8183
Self::MultiProcessor => "uefi-test-runner/multi_processor",
@@ -117,7 +119,7 @@ impl Feature {
117119
/// - `include_unstable` - add all functionality behind the `unstable` feature
118120
/// - `runtime_features` - add all functionality that effect the runtime of Rust
119121
pub fn more_code(include_unstable: bool, runtime_features: bool) -> Vec<Self> {
120-
let mut base_features = vec![Self::Alloc, Self::LogDebugcon, Self::Logger];
122+
let mut base_features = vec![Self::Alloc, Self::LogDebugcon, Self::Logger, Self::Time03];
121123
if include_unstable {
122124
base_features.extend([Self::Unstable])
123125
}
@@ -387,19 +389,19 @@ mod tests {
387389
fn test_comma_separated_features() {
388390
assert_eq!(
389391
Feature::comma_separated_string(&Feature::more_code(false, false)),
390-
"alloc,log-debugcon,logger"
392+
"alloc,log-debugcon,logger,time03"
391393
);
392394
assert_eq!(
393395
Feature::comma_separated_string(&Feature::more_code(false, true)),
394-
"alloc,log-debugcon,logger,global_allocator"
396+
"alloc,log-debugcon,logger,time03,global_allocator"
395397
);
396398
assert_eq!(
397399
Feature::comma_separated_string(&Feature::more_code(true, false)),
398-
"alloc,log-debugcon,logger,unstable"
400+
"alloc,log-debugcon,logger,time03,unstable"
399401
);
400402
assert_eq!(
401403
Feature::comma_separated_string(&Feature::more_code(true, true)),
402-
"alloc,log-debugcon,logger,unstable,global_allocator"
404+
"alloc,log-debugcon,logger,time03,unstable,global_allocator"
403405
);
404406
}
405407

0 commit comments

Comments
 (0)