uefi: add jiff02 feature and integrate jiff types with struct Time

This commit is contained in:
Philipp Schuster 2026-03-15 15:48:10 +01:00
parent da39859044
commit afdd47bf40
No known key found for this signature in database
9 changed files with 204 additions and 6 deletions

38
Cargo.lock generated
View file

@ -435,6 +435,28 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jiff"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359"
dependencies = [
"jiff-static",
"portable-atomic",
"portable-atomic-util",
]
[[package]]
name = "jiff-static"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "leb128fmt" name = "leb128fmt"
version = "0.1.0" version = "0.1.0"
@ -538,6 +560,21 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@ -988,6 +1025,7 @@ version = "0.37.0"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.11.1",
"cfg-if", "cfg-if",
"jiff",
"log", "log",
"ptr_meta", "ptr_meta",
"qemu-exit", "qemu-exit",

View file

@ -22,6 +22,7 @@ rust-version = "1.91"
[workspace.dependencies] [workspace.dependencies]
bitflags = "2.0.0" bitflags = "2.0.0"
log = { version = "0.4.5", default-features = false } log = { version = "0.4.5", default-features = false }
jiff = { version = "0.2", default-features = false }
ptr_meta = { version = "0.3.0", default-features = false, features = ["derive"] } ptr_meta = { version = "0.3.0", default-features = false, features = ["derive"] }
qemu-exit = { version = "4.0.0" } qemu-exit = { version = "4.0.0" }
time = { version = "0.3", default-features = false } time = { version = "0.3", default-features = false }

View file

@ -47,6 +47,9 @@
- Integration of `Time` with `time` crate - Integration of `Time` with `time` crate
- `TryFrom`: `time::PrimitiveDateTime <--> Time` (without timezone) - `TryFrom`: `time::PrimitiveDateTime <--> Time` (without timezone)
- `TryFrom`: `time::OffsetDateTime <--> Time` (with timezone) - `TryFrom`: `time::OffsetDateTime <--> Time` (with timezone)
- Integration of `Time` with `jiff` crate
- `TryFrom`: `jiff::DateTime <--> Time` (without timezone)
- `TryFrom`: `jiff::Zoned <--> Time` (with timezone)
## Changed ## Changed
- export all `text::{input, output}::*` types - export all `text::{input, output}::*` types

View file

@ -20,6 +20,8 @@ rust-version.workspace = true
# KEEP this feature list in sync with doc in uefi/lib.rs! # KEEP this feature list in sync with doc in uefi/lib.rs!
default = [ ] default = [ ]
alloc = [] alloc = []
# Integration with jiff crate `v0.2`
jiff02 = ["dep:jiff"]
# Integration with time crate `v0.3` # Integration with time crate `v0.3`
time03 = ["dep:time"] time03 = ["dep:time"]
@ -41,6 +43,7 @@ log-debugcon = []
bitflags.workspace = true bitflags.workspace = true
log.workspace = true log.workspace = true
ptr_meta.workspace = true ptr_meta.workspace = true
jiff = { workspace = true, optional = true }
time = { workspace = true, optional = true } time = { workspace = true, optional = true }
qemu-exit = { workspace = true, optional = true } qemu-exit = { workspace = true, optional = true }
uguid.workspace = true uguid.workspace = true

View file

@ -142,6 +142,9 @@
//! using this feature, or no allocator at all if you don't need to //! using this feature, or no allocator at all if you don't need to
//! dynamically allocate any memory. Note that even without that feature, //! dynamically allocate any memory. Note that even without that feature,
//! some code might use the internal UEFI allocator. //! some code might use the internal UEFI allocator.
//! - `jiff02`: Integration of [`runtime::Time`] with the `jiff` crate
//! (version 0.2 and possible above). Specifically, it integrates the time
//! struct with `DateTime` and `Zoned` via `TryFrom`.
//! - `logger`: Logging implementation for the standard [`log`] crate //! - `logger`: Logging implementation for the standard [`log`] crate
//! that prints output to the UEFI console. No buffering is done; this //! that prints output to the UEFI console. No buffering is done; this
//! is not a high-performance logger. //! is not a high-performance logger.

View file

@ -38,6 +38,9 @@ pub(super) enum ConversionErrorInner {
/// Errors raised in the [`time`] crate. /// Errors raised in the [`time`] crate.
#[cfg(feature = "time03")] #[cfg(feature = "time03")]
TimeCrateError(time::Error), TimeCrateError(time::Error),
/// Errors raised in the [`jiff`] crate.
#[cfg(feature = "jiff02")]
JiffCrateError(jiff::Error),
} }
impl Display for ConversionErrorInner { impl Display for ConversionErrorInner {
@ -48,6 +51,8 @@ impl Display for ConversionErrorInner {
Self::UnspecifiedTimezone => write!(f, "Unspecified timezone"), Self::UnspecifiedTimezone => write!(f, "Unspecified timezone"),
#[cfg(feature = "time03")] #[cfg(feature = "time03")]
Self::TimeCrateError(e) => write!(f, "Time crate error: {}", e), Self::TimeCrateError(e) => write!(f, "Time crate error: {}", e),
#[cfg(feature = "jiff02")]
Self::JiffCrateError(e) => write!(f, "Jiff crate error: {}", e),
} }
} }
} }
@ -60,6 +65,9 @@ impl Error for ConversionErrorInner {
Self::UnspecifiedTimezone => None, Self::UnspecifiedTimezone => None,
#[cfg(feature = "time03")] #[cfg(feature = "time03")]
Self::TimeCrateError(e) => Some(e), Self::TimeCrateError(e) => Some(e),
// None: Missing Error trait
#[cfg(feature = "jiff02")]
Self::JiffCrateError(_e) => None,
} }
} }
} }

View file

@ -0,0 +1,126 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Integration of the UEFI [`Time`] type with the [`jiff`] crate.
use super::Time;
use super::integration_common::{ConversionErrorInner, TimeConversionError};
use jiff::Zoned;
use jiff::civil::DateTime;
use jiff::tz::{Offset, TimeZone};
use uefi::runtime::TimeParams;
// Timezone unaware
impl TryFrom<Time> for DateTime {
type Error = TimeConversionError;
fn try_from(value: Time) -> Result<Self, Self::Error> {
if let Err(e) = value.is_valid() {
return Err(TimeConversionError(ConversionErrorInner::InvalidUefiTime(
e,
)));
}
let datetime = Self::new(
// Cannot fail as the value is valid and in range (we checked that).
i16::try_from(value.0.year).unwrap(),
// Cannot fail as the value is valid and in range (we checked that).
i8::try_from(value.0.month).unwrap(),
// Cannot fail as the value is valid and in range (we checked that).
i8::try_from(value.0.day).unwrap(),
// Cannot fail as the value is valid and in range (we checked that).
i8::try_from(value.0.hour).unwrap(),
// Cannot fail as the value is valid and in range (we checked that).
i8::try_from(value.0.minute).unwrap(),
// Cannot fail as the value is valid and in range (we checked that).
i8::try_from(value.0.second).unwrap(),
// Cannot fail as the value is valid and in range (we checked that).
i32::try_from(value.0.nanosecond).unwrap(),
)
.map_err(|e| TimeConversionError(ConversionErrorInner::JiffCrateError(e)))?;
Ok(datetime)
}
}
// Timezone aware
impl TryFrom<Time> for Zoned {
type Error = TimeConversionError;
fn try_from(value: Time) -> Result<Self, Self::Error> {
if let Err(e) = value.is_valid() {
return Err(TimeConversionError(ConversionErrorInner::InvalidUefiTime(
e,
)));
}
if value.0.time_zone == Time::UNSPECIFIED_TIMEZONE {
return Err(TimeConversionError(
ConversionErrorInner::UnspecifiedTimezone,
));
}
let datetime = DateTime::try_from(value)?;
let seconds = value.0.time_zone as i32 * 60 /* seconds per minute */;
let offset = Offset::from_seconds(seconds)
.map_err(|e| TimeConversionError(ConversionErrorInner::JiffCrateError(e)))?;
let timezone = TimeZone::fixed(offset);
let zoned = datetime
.to_zoned(timezone)
.map_err(|e| TimeConversionError(ConversionErrorInner::JiffCrateError(e)))?;
Ok(zoned)
}
}
impl TryFrom<DateTime> for Time {
type Error = TimeConversionError;
fn try_from(value: DateTime) -> Result<Self, Self::Error> {
let params = TimeParams {
year: u16::try_from(value.year())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
month: u8::try_from(value.month())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
day: u8::try_from(value.day())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
hour: u8::try_from(value.hour())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
minute: u8::try_from(value.minute())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
second: u8::try_from(value.second())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
nanosecond: u32::try_from(value.subsec_nanosecond())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
time_zone: None,
daylight: Default::default(),
};
Self::new(params).map_err(|e| TimeConversionError(ConversionErrorInner::InvalidUefiTime(e)))
}
}
impl TryFrom<Zoned> for Time {
type Error = TimeConversionError;
fn try_from(value: Zoned) -> Result<Self, Self::Error> {
let params = TimeParams {
year: u16::try_from(value.year())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
month: u8::try_from(value.month())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
day: u8::try_from(value.day())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
hour: u8::try_from(value.hour())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
minute: u8::try_from(value.minute())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
second: u8::try_from(value.second())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
nanosecond: u32::try_from(value.subsec_nanosecond())
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
time_zone: Some(
i16::try_from(value.offset().seconds() / 60 /* seconds per minute */)
.map_err(|_e| TimeConversionError(ConversionErrorInner::InvalidComponent))?,
),
daylight: Default::default(),
};
Self::new(params).map_err(|e| TimeConversionError(ConversionErrorInner::InvalidUefiTime(e)))
}
}

View file

@ -10,8 +10,10 @@ use core::fmt;
use core::fmt::{Debug, Display, Formatter}; use core::fmt::{Debug, Display, Formatter};
use uefi_raw::time::Daylight; use uefi_raw::time::Daylight;
#[cfg(feature = "time03")] #[cfg(any(feature = "jiff02", feature = "time03"))]
mod integration_common; mod integration_common;
#[cfg(feature = "jiff02")]
mod integration_jiff_crate;
#[cfg(feature = "time03")] #[cfg(feature = "time03")]
mod integration_time_crate; mod integration_time_crate;
@ -28,7 +30,13 @@ mod integration_time_crate;
/// - [`TryFrom`]: `PrimitiveDateTime` <--> [`Time`] (without timezone) /// - [`TryFrom`]: `PrimitiveDateTime` <--> [`Time`] (without timezone)
/// - [`TryFrom`]: `OffsetDateTime` <--> [`Time`] (with timezone) /// - [`TryFrom`]: `OffsetDateTime` <--> [`Time`] (with timezone)
/// ///
/// ## Integration with [`jiff`][jiff crate] crate
///
/// - [`TryFrom`]: `DateTime` <--> [`Time`] (without timezone)
/// - [`TryFrom`]: `Zoned` <--> [`Time`] (with timezone)
///
/// [time crate]: https://crates.io/crates/time /// [time crate]: https://crates.io/crates/time
/// [jiff crate]: https://crates.io/crates/jiff
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq)]
#[repr(transparent)] #[repr(transparent)]
pub struct Time(uefi_raw::time::Time); pub struct Time(uefi_raw::time::Time);

View file

@ -56,6 +56,7 @@ pub enum Feature {
Unstable, Unstable,
PanicHandler, PanicHandler,
Qemu, Qemu,
Jiff02,
Time03, Time03,
// `uefi-test-runner` features. // `uefi-test-runner` features.
@ -77,6 +78,7 @@ impl Feature {
Self::Unstable => "unstable", Self::Unstable => "unstable",
Self::PanicHandler => "panic_handler", Self::PanicHandler => "panic_handler",
Self::Qemu => "qemu", Self::Qemu => "qemu",
Self::Jiff02 => "jiff02",
Self::Time03 => "time03", Self::Time03 => "time03",
Self::DebugSupport => "uefi-test-runner/debug_support", Self::DebugSupport => "uefi-test-runner/debug_support",
@ -119,7 +121,13 @@ impl Feature {
/// - `include_unstable` - add all functionality behind the `unstable` feature /// - `include_unstable` - add all functionality behind the `unstable` feature
/// - `runtime_features` - add all functionality that effect the runtime of Rust /// - `runtime_features` - add all functionality that effect the runtime of Rust
pub fn more_code(include_unstable: bool, runtime_features: bool) -> Vec<Self> { pub fn more_code(include_unstable: bool, runtime_features: bool) -> Vec<Self> {
let mut base_features = vec![Self::Alloc, Self::LogDebugcon, Self::Logger, Self::Time03]; let mut base_features = vec![
Self::Alloc,
Self::Jiff02,
Self::LogDebugcon,
Self::Logger,
Self::Time03,
];
if include_unstable { if include_unstable {
base_features.extend([Self::Unstable]) base_features.extend([Self::Unstable])
} }
@ -389,19 +397,19 @@ mod tests {
fn test_comma_separated_features() { fn test_comma_separated_features() {
assert_eq!( assert_eq!(
Feature::comma_separated_string(&Feature::more_code(false, false)), Feature::comma_separated_string(&Feature::more_code(false, false)),
"alloc,log-debugcon,logger,time03" "alloc,jiff02,log-debugcon,logger,time03"
); );
assert_eq!( assert_eq!(
Feature::comma_separated_string(&Feature::more_code(false, true)), Feature::comma_separated_string(&Feature::more_code(false, true)),
"alloc,log-debugcon,logger,time03,global_allocator" "alloc,jiff02,log-debugcon,logger,time03,global_allocator"
); );
assert_eq!( assert_eq!(
Feature::comma_separated_string(&Feature::more_code(true, false)), Feature::comma_separated_string(&Feature::more_code(true, false)),
"alloc,log-debugcon,logger,time03,unstable" "alloc,jiff02,log-debugcon,logger,time03,unstable"
); );
assert_eq!( assert_eq!(
Feature::comma_separated_string(&Feature::more_code(true, true)), Feature::comma_separated_string(&Feature::more_code(true, true)),
"alloc,log-debugcon,logger,time03,unstable,global_allocator" "alloc,jiff02,log-debugcon,logger,time03,unstable,global_allocator"
); );
} }