Skip to content

Add Time to uefi-raw and use it from uefi #817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions uefi-raw/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod enums;

pub mod protocol;
pub mod table;
pub mod time;

mod status;

Expand Down
170 changes: 170 additions & 0 deletions uefi-raw/src/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//! Date and time types.

use bitflags::bitflags;
use core::fmt::{self, Display, Formatter};

/// Date and time representation.
#[derive(Debug, Default, Copy, Clone, Eq)]
#[repr(C)]
pub struct Time {
/// Year. Valid range: `1900..=9999`.
pub year: u16,

/// Month. Valid range: `1..=12`.
pub month: u8,

/// Day of the month. Valid range: `1..=31`.
pub day: u8,

/// Hour. Valid range: `0..=23`.
pub hour: u8,

/// Minute. Valid range: `0..=59`.
pub minute: u8,

/// Second. Valid range: `0..=59`.
pub second: u8,

/// Unused padding.
pub pad1: u8,

/// Nanosececond. Valid range: `0..=999_999_999`.
pub nanosecond: u32,

/// Offset in minutes from UTC. Valid range: `-1440..=1440`, or
/// [`Time::UNSPECIFIED_TIMEZONE`].
pub time_zone: i16,

/// Daylight savings time information.
pub daylight: Daylight,

/// Unused padding.
pub pad2: u8,
}

impl Time {
/// Indicates the time should be interpreted as local time.
pub const UNSPECIFIED_TIMEZONE: i16 = 0x07ff;

/// Create an invalid `Time` with all fields set to zero.
#[must_use]
pub const fn invalid() -> Self {
Self {
year: 0,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
pad1: 0,
nanosecond: 0,
time_zone: 0,
daylight: Daylight::empty(),
pad2: 0,
}
}

/// True if all fields are within valid ranges, false otherwise.
#[must_use]
pub fn is_valid(&self) -> bool {
(1900..=9999).contains(&self.year)
&& (1..=12).contains(&self.month)
&& (1..=31).contains(&self.day)
&& self.hour <= 23
&& self.minute <= 59
&& self.second <= 59
&& self.nanosecond <= 999_999_999
&& ((-1440..=1440).contains(&self.time_zone)
|| self.time_zone == Self::UNSPECIFIED_TIMEZONE)
}
}

impl Display for Time {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02} ", self.year, self.month, self.day)?;
write!(
f,
"{:02}:{:02}:{:02}.{:09}",
self.hour, self.minute, self.second, self.nanosecond
)?;

if self.time_zone == Self::UNSPECIFIED_TIMEZONE {
write!(f, " (local)")?;
} else {
let offset_in_hours = self.time_zone as f32 / 60.0;
let integer_part = offset_in_hours as i16;
// We can't use "offset_in_hours.fract()" because it is part of `std`.
let fraction_part = offset_in_hours - (integer_part as f32);
// most time zones
if fraction_part == 0.0 {
write!(f, "UTC+{offset_in_hours}")?;
}
// time zones with 30min offset (and perhaps other special time zones)
else {
write!(f, "UTC+{offset_in_hours:.1}")?;
}
}

Ok(())
}
}

/// The padding fields of `Time` are ignored for comparison.
impl PartialEq for Time {
fn eq(&self, other: &Time) -> bool {
self.year == other.year
&& self.month == other.month
&& self.day == other.day
&& self.hour == other.hour
&& self.minute == other.minute
&& self.second == other.second
&& self.nanosecond == other.nanosecond
&& self.time_zone == other.time_zone
&& self.daylight == other.daylight
}
}

bitflags! {
/// A bitmask containing daylight savings time information.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Daylight: u8 {
/// Time is affected by daylight savings time.
const ADJUST_DAYLIGHT = 0x01;

/// Time has been adjusted for daylight savings time.
const IN_DAYLIGHT = 0x02;
}
}

#[cfg(test)]
mod tests {
extern crate alloc;

use super::*;
use alloc::string::ToString;

#[test]
fn test_time_display() {
let mut time = Time {
year: 2023,
month: 5,
day: 18,
hour: 11,
minute: 29,
second: 57,
nanosecond: 123_456_789,
time_zone: Time::UNSPECIFIED_TIMEZONE,
daylight: Daylight::empty(),
pad1: 0,
pad2: 0,
};
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789 (local)");

time.time_zone = 120;
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2");

time.time_zone = 150;
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2.5");
}
}
Loading