uefi_raw/
time.rs

1//! Date and time types.
2
3use bitflags::bitflags;
4use core::fmt::{self, Display, Formatter};
5
6/// Date and time representation.
7#[derive(Debug, Default, Copy, Clone, Eq)]
8#[repr(C)]
9pub 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
45impl 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
82impl 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.
113impl PartialEq for Time {
114    fn eq(&self, other: &Self) -> 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
127bitflags! {
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)]
141mod 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}