Skip to content

Commit 22afe4f

Browse files
committed
uefi: extract time code to new module
I split the time-related type definitions from the time-related runtime UEFI services. This is a preparation to integrate the popular time-related crates `jiff` and `time` with `struct Time` of the `uefi` crate.
1 parent 77f68bf commit 22afe4f

2 files changed

Lines changed: 326 additions & 312 deletions

File tree

uefi/src/runtime/mod.rs

Lines changed: 7 additions & 312 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77
//! functions after exiting boot services; see the "Calling Convention" section
88
//! of the UEFI specification for details.
99
10+
mod time_defs;
11+
12+
pub use time_defs::{Time, TimeByteConversionError, TimeError, TimeParams};
13+
1014
use crate::data_types::PhysicalAddress;
1115
use crate::table::{self, Revision};
1216
use crate::{CStr16, Error, Result, Status, StatusExt};
13-
use core::fmt::{self, Debug, Display, Formatter};
17+
use core::fmt::Debug;
18+
#[cfg(feature = "alloc")]
19+
use core::fmt::{self, Display, Formatter};
1420
use core::ptr::{self, NonNull};
1521
use uefi_raw::table::boot::MemoryDescriptor;
1622

@@ -549,317 +555,6 @@ pub unsafe fn set_virtual_address_map(
549555
Ok(())
550556
}
551557

552-
/// Date and time representation.
553-
#[derive(Copy, Clone, Eq, PartialEq)]
554-
#[repr(transparent)]
555-
pub struct Time(uefi_raw::time::Time);
556-
557-
/// Input parameters for [`Time::new`].
558-
#[derive(Copy, Clone, Debug)]
559-
pub struct TimeParams {
560-
/// Year in the range `1900..=9999`.
561-
pub year: u16,
562-
563-
/// Month in the range `1..=12`.
564-
pub month: u8,
565-
566-
/// Day in the range `1..=31`.
567-
pub day: u8,
568-
569-
/// Hour in the range `0.=23`.
570-
pub hour: u8,
571-
572-
/// Minute in the range `0..=59`.
573-
pub minute: u8,
574-
575-
/// Second in the range `0..=59`.
576-
pub second: u8,
577-
578-
/// Fraction of a second represented as nanoseconds in the range
579-
/// `0..=999_999_999`.
580-
pub nanosecond: u32,
581-
582-
/// Offset in minutes from UTC in the range `-1440..=1440`, or
583-
/// local time if `None`.
584-
pub time_zone: Option<i16>,
585-
586-
/// Daylight savings time information.
587-
pub daylight: Daylight,
588-
}
589-
590-
/// Error returned by [`Time`] methods. A bool value of `true` means
591-
/// the specified field is outside its valid range.
592-
#[allow(missing_docs)]
593-
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
594-
pub struct TimeError {
595-
pub year: bool,
596-
pub month: bool,
597-
pub day: bool,
598-
pub hour: bool,
599-
pub minute: bool,
600-
pub second: bool,
601-
pub nanosecond: bool,
602-
pub timezone: bool,
603-
pub daylight: bool,
604-
}
605-
606-
impl core::error::Error for TimeError {}
607-
608-
impl Display for TimeError {
609-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
610-
if self.year {
611-
writeln!(f, "year not within `1900..=9999`")?;
612-
}
613-
if self.month {
614-
writeln!(f, "month not within `1..=12")?;
615-
}
616-
if self.day {
617-
writeln!(f, "day not within `1..=31`")?;
618-
}
619-
if self.hour {
620-
writeln!(f, "hour not within `0..=23`")?;
621-
}
622-
if self.minute {
623-
writeln!(f, "minute not within `0..=59`")?;
624-
}
625-
if self.second {
626-
writeln!(f, "second not within `0..=59`")?;
627-
}
628-
if self.nanosecond {
629-
writeln!(f, "nanosecond not within `0..=999_999_999`")?;
630-
}
631-
if self.timezone {
632-
writeln!(
633-
f,
634-
"time_zone not `Time::UNSPECIFIED_TIMEZONE` nor within `-1440..=1440`"
635-
)?;
636-
}
637-
if self.daylight {
638-
writeln!(f, "unknown bits set for daylight")?;
639-
}
640-
Ok(())
641-
}
642-
}
643-
644-
impl Time {
645-
/// Unspecified Timezone/local time.
646-
const UNSPECIFIED_TIMEZONE: i16 = uefi_raw::time::Time::UNSPECIFIED_TIMEZONE;
647-
648-
/// Create a `Time` value. If a field is not in the valid range,
649-
/// [`TimeError`] is returned.
650-
pub fn new(params: TimeParams) -> core::result::Result<Self, TimeError> {
651-
let time = Self(uefi_raw::time::Time {
652-
year: params.year,
653-
month: params.month,
654-
day: params.day,
655-
hour: params.hour,
656-
minute: params.minute,
657-
second: params.second,
658-
pad1: 0,
659-
nanosecond: params.nanosecond,
660-
time_zone: params.time_zone.unwrap_or(Self::UNSPECIFIED_TIMEZONE),
661-
daylight: params.daylight,
662-
pad2: 0,
663-
});
664-
665-
time.is_valid().map(|_| time)
666-
}
667-
668-
/// Create an invalid `Time` with all fields set to zero. This can
669-
/// be used with [`FileInfo`] to indicate a field should not be
670-
/// updated when calling [`File::set_info`].
671-
///
672-
/// [`FileInfo`]: uefi::proto::media::file::FileInfo
673-
/// [`File::set_info`]: uefi::proto::media::file::File::set_info
674-
#[must_use]
675-
pub const fn invalid() -> Self {
676-
Self(uefi_raw::time::Time::invalid())
677-
}
678-
679-
/// `Ok()` if all fields are within valid ranges, `Err(TimeError)` otherwise.
680-
pub fn is_valid(&self) -> core::result::Result<(), TimeError> {
681-
let mut err = TimeError::default();
682-
if !(1900..=9999).contains(&self.year()) {
683-
err.year = true;
684-
}
685-
if !(1..=12).contains(&self.month()) {
686-
err.month = true;
687-
}
688-
if !(1..=31).contains(&self.day()) {
689-
err.day = true;
690-
}
691-
if self.hour() > 23 {
692-
err.hour = true;
693-
}
694-
if self.minute() > 59 {
695-
err.minute = true;
696-
}
697-
if self.second() > 59 {
698-
err.second = true;
699-
}
700-
if self.nanosecond() > 999_999_999 {
701-
err.nanosecond = true;
702-
}
703-
if self.time_zone().is_some() && !((-1440..=1440).contains(&self.time_zone().unwrap())) {
704-
err.timezone = true;
705-
}
706-
// All fields are false, i.e., within their valid range.
707-
if err == TimeError::default() {
708-
Ok(())
709-
} else {
710-
Err(err)
711-
}
712-
}
713-
714-
/// Query the year.
715-
#[must_use]
716-
pub const fn year(&self) -> u16 {
717-
self.0.year
718-
}
719-
720-
/// Query the month.
721-
#[must_use]
722-
pub const fn month(&self) -> u8 {
723-
self.0.month
724-
}
725-
726-
/// Query the day.
727-
#[must_use]
728-
pub const fn day(&self) -> u8 {
729-
self.0.day
730-
}
731-
732-
/// Query the hour.
733-
#[must_use]
734-
pub const fn hour(&self) -> u8 {
735-
self.0.hour
736-
}
737-
738-
/// Query the minute.
739-
#[must_use]
740-
pub const fn minute(&self) -> u8 {
741-
self.0.minute
742-
}
743-
744-
/// Query the second.
745-
#[must_use]
746-
pub const fn second(&self) -> u8 {
747-
self.0.second
748-
}
749-
750-
/// Query the nanosecond.
751-
#[must_use]
752-
pub const fn nanosecond(&self) -> u32 {
753-
self.0.nanosecond
754-
}
755-
756-
/// Query the time offset in minutes from UTC, or None if using local time.
757-
#[must_use]
758-
pub const fn time_zone(&self) -> Option<i16> {
759-
if self.0.time_zone == Self::UNSPECIFIED_TIMEZONE {
760-
None
761-
} else {
762-
Some(self.0.time_zone)
763-
}
764-
}
765-
766-
/// Query the daylight savings time information.
767-
#[must_use]
768-
pub const fn daylight(&self) -> Daylight {
769-
self.0.daylight
770-
}
771-
}
772-
773-
impl Debug for Time {
774-
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
775-
write!(
776-
f,
777-
"{:04}-{:02}-{:02} ",
778-
self.0.year, self.0.month, self.0.day
779-
)?;
780-
write!(
781-
f,
782-
"{:02}:{:02}:{:02}.{:09}",
783-
self.0.hour, self.0.minute, self.0.second, self.0.nanosecond
784-
)?;
785-
if self.0.time_zone == Self::UNSPECIFIED_TIMEZONE {
786-
write!(f, ", Timezone=local")?;
787-
} else {
788-
write!(f, ", Timezone={}", self.0.time_zone)?;
789-
}
790-
write!(f, ", Daylight={:?}", self.0.daylight)
791-
}
792-
}
793-
794-
impl Display for Time {
795-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
796-
write!(f, "{}", self.0)
797-
}
798-
}
799-
800-
/// Error returned from failing to convert a byte slice into a [`Time`].
801-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
802-
pub enum TimeByteConversionError {
803-
/// One or more fields of the converted [`Time`] is invalid.
804-
InvalidFields(TimeError),
805-
/// The byte slice is not large enough to hold a [`Time`].
806-
InvalidSize,
807-
}
808-
809-
impl Display for TimeByteConversionError {
810-
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
811-
match self {
812-
Self::InvalidFields(error) => write!(f, "{error}"),
813-
Self::InvalidSize => write!(
814-
f,
815-
"the byte slice is not large enough to hold a Time struct"
816-
),
817-
}
818-
}
819-
}
820-
821-
impl TryFrom<&[u8]> for Time {
822-
type Error = TimeByteConversionError;
823-
824-
fn try_from(bytes: &[u8]) -> core::result::Result<Self, Self::Error> {
825-
if size_of::<Self>() <= bytes.len() {
826-
let year = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
827-
let month = bytes[2];
828-
let day = bytes[3];
829-
let hour = bytes[4];
830-
let minute = bytes[5];
831-
let second = bytes[6];
832-
let nanosecond = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
833-
let time_zone = match i16::from_le_bytes(bytes[12..14].try_into().unwrap()) {
834-
Self::UNSPECIFIED_TIMEZONE => None,
835-
num => Some(num),
836-
};
837-
let daylight = Daylight::from_bits(bytes[14]).ok_or_else(|| {
838-
TimeByteConversionError::InvalidFields(TimeError {
839-
daylight: true,
840-
..Default::default()
841-
})
842-
})?;
843-
844-
let time_params = TimeParams {
845-
year,
846-
month,
847-
day,
848-
hour,
849-
minute,
850-
second,
851-
nanosecond,
852-
time_zone,
853-
daylight,
854-
};
855-
856-
Self::new(time_params).map_err(TimeByteConversionError::InvalidFields)
857-
} else {
858-
Err(TimeByteConversionError::InvalidSize)
859-
}
860-
}
861-
}
862-
863558
/// Unique key for a variable.
864559
#[cfg(feature = "alloc")]
865560
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]

0 commit comments

Comments
 (0)