Skip to content

Commit 82e73e0

Browse files
authored
Merge pull request #817 from nicholasbishop/bishop-raw-time
Add Time to uefi-raw and use it from uefi
2 parents 0592259 + bcbe10a commit 82e73e0

File tree

3 files changed

+202
-111
lines changed

3 files changed

+202
-111
lines changed

uefi-raw/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod enums;
1919

2020
pub mod protocol;
2121
pub mod table;
22+
pub mod time;
2223

2324
mod status;
2425

uefi-raw/src/time.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//! Date and time types.
2+
3+
use bitflags::bitflags;
4+
use core::fmt::{self, Display, Formatter};
5+
6+
/// Date and time representation.
7+
#[derive(Debug, Default, Copy, Clone, Eq)]
8+
#[repr(C)]
9+
pub struct Time {
10+
/// Year. Valid range: `1900..=9999`.
11+
pub year: u16,
12+
13+
/// Month. Valid range: `1..=12`.
14+
pub month: u8,
15+
16+
/// Day of the month. Valid range: `1..=31`.
17+
pub day: u8,
18+
19+
/// Hour. Valid range: `0..=23`.
20+
pub hour: u8,
21+
22+
/// Minute. Valid range: `0..=59`.
23+
pub minute: u8,
24+
25+
/// Second. Valid range: `0..=59`.
26+
pub second: u8,
27+
28+
/// Unused padding.
29+
pub pad1: u8,
30+
31+
/// Nanosececond. Valid range: `0..=999_999_999`.
32+
pub nanosecond: u32,
33+
34+
/// Offset in minutes from UTC. Valid range: `-1440..=1440`, or
35+
/// [`Time::UNSPECIFIED_TIMEZONE`].
36+
pub time_zone: i16,
37+
38+
/// Daylight savings time information.
39+
pub daylight: Daylight,
40+
41+
/// Unused padding.
42+
pub pad2: u8,
43+
}
44+
45+
impl Time {
46+
/// Indicates the time should be interpreted as local time.
47+
pub const UNSPECIFIED_TIMEZONE: i16 = 0x07ff;
48+
49+
/// Create an invalid `Time` with all fields set to zero.
50+
#[must_use]
51+
pub const fn invalid() -> Self {
52+
Self {
53+
year: 0,
54+
month: 0,
55+
day: 0,
56+
hour: 0,
57+
minute: 0,
58+
second: 0,
59+
pad1: 0,
60+
nanosecond: 0,
61+
time_zone: 0,
62+
daylight: Daylight::empty(),
63+
pad2: 0,
64+
}
65+
}
66+
67+
/// True if all fields are within valid ranges, false otherwise.
68+
#[must_use]
69+
pub fn is_valid(&self) -> bool {
70+
(1900..=9999).contains(&self.year)
71+
&& (1..=12).contains(&self.month)
72+
&& (1..=31).contains(&self.day)
73+
&& self.hour <= 23
74+
&& self.minute <= 59
75+
&& self.second <= 59
76+
&& self.nanosecond <= 999_999_999
77+
&& ((-1440..=1440).contains(&self.time_zone)
78+
|| self.time_zone == Self::UNSPECIFIED_TIMEZONE)
79+
}
80+
}
81+
82+
impl Display for Time {
83+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84+
write!(f, "{:04}-{:02}-{:02} ", self.year, self.month, self.day)?;
85+
write!(
86+
f,
87+
"{:02}:{:02}:{:02}.{:09}",
88+
self.hour, self.minute, self.second, self.nanosecond
89+
)?;
90+
91+
if self.time_zone == Self::UNSPECIFIED_TIMEZONE {
92+
write!(f, " (local)")?;
93+
} else {
94+
let offset_in_hours = self.time_zone as f32 / 60.0;
95+
let integer_part = offset_in_hours as i16;
96+
// We can't use "offset_in_hours.fract()" because it is part of `std`.
97+
let fraction_part = offset_in_hours - (integer_part as f32);
98+
// most time zones
99+
if fraction_part == 0.0 {
100+
write!(f, "UTC+{offset_in_hours}")?;
101+
}
102+
// time zones with 30min offset (and perhaps other special time zones)
103+
else {
104+
write!(f, "UTC+{offset_in_hours:.1}")?;
105+
}
106+
}
107+
108+
Ok(())
109+
}
110+
}
111+
112+
/// The padding fields of `Time` are ignored for comparison.
113+
impl PartialEq for Time {
114+
fn eq(&self, other: &Time) -> bool {
115+
self.year == other.year
116+
&& self.month == other.month
117+
&& self.day == other.day
118+
&& self.hour == other.hour
119+
&& self.minute == other.minute
120+
&& self.second == other.second
121+
&& self.nanosecond == other.nanosecond
122+
&& self.time_zone == other.time_zone
123+
&& self.daylight == other.daylight
124+
}
125+
}
126+
127+
bitflags! {
128+
/// A bitmask containing daylight savings time information.
129+
#[repr(transparent)]
130+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
131+
pub struct Daylight: u8 {
132+
/// Time is affected by daylight savings time.
133+
const ADJUST_DAYLIGHT = 0x01;
134+
135+
/// Time has been adjusted for daylight savings time.
136+
const IN_DAYLIGHT = 0x02;
137+
}
138+
}
139+
140+
#[cfg(test)]
141+
mod tests {
142+
extern crate alloc;
143+
144+
use super::*;
145+
use alloc::string::ToString;
146+
147+
#[test]
148+
fn test_time_display() {
149+
let mut time = Time {
150+
year: 2023,
151+
month: 5,
152+
day: 18,
153+
hour: 11,
154+
minute: 29,
155+
second: 57,
156+
nanosecond: 123_456_789,
157+
time_zone: Time::UNSPECIFIED_TIMEZONE,
158+
daylight: Daylight::empty(),
159+
pad1: 0,
160+
pad2: 0,
161+
};
162+
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789 (local)");
163+
164+
time.time_zone = 120;
165+
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2");
166+
167+
time.time_zone = 150;
168+
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2.5");
169+
}
170+
}

0 commit comments

Comments
 (0)