1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! UEFI Time Services.

use bitfield_struct::bitfield;
use core::fmt::Display;
use static_assertions::const_assert_eq;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;

/// UEFI Time Structure
///
/// UEFI spec 8.3 - Time Services
///
/// ```text
///  Year:       1900 - 9999
///  Month:      1 - 12
///  Day:        1 - 31
///  Hour:       0 - 23
///  Minute:     0 - 59
///  Second:     0 - 59
///  Nanosecond: 0 - 999,999,999
///  TimeZone:   -1440 to 1440 or 2047
/// ```
#[repr(C)]
#[derive(Copy, Clone, Debug, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
#[cfg_attr(feature = "inspect", derive(inspect::Inspect), inspect(display))]
pub struct EFI_TIME {
    pub year: u16,
    pub month: u8,
    pub day: u8,
    pub hour: u8,
    pub minute: u8,
    pub second: u8,
    pub pad1: u8,
    pub nanosecond: u32,
    pub timezone: EfiTimezone,
    pub daylight: EfiDaylight,
    pub pad2: u8,
}

// Default + FromBytes: The UEFI spec explicitly uses all-zero EFI_TIME as a
// default value
impl Default for EFI_TIME {
    fn default() -> Self {
        Self::new_zeroed()
    }
}

const_assert_eq!(size_of::<EFI_TIME>(), 16);

impl EFI_TIME {
    /// EFI_TIME with all fields set to zero
    pub const ZEROED: EFI_TIME = EFI_TIME {
        year: 0,
        month: 0,
        day: 0,
        hour: 0,
        minute: 0,
        second: 0,
        pad1: 0,
        nanosecond: 0,
        timezone: EfiTimezone(0),
        daylight: EfiDaylight::new(),
        pad2: 0,
    };
}

impl Display for EFI_TIME {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        // ISO-8601: 2022-02-17T04:54:13Z
        write!(
            f,
            "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
            self.year, self.month, self.day, self.hour, self.minute, self.second
        )?;
        if self.nanosecond != 0 {
            write!(f, ".{:09}", self.nanosecond)?;
        }
        if self.timezone.0 == 0 {
            write!(f, "Z")?;
        } else if self.timezone != EFI_UNSPECIFIED_TIMEZONE {
            let sign = if self.timezone.0 > 0 { '+' } else { '-' };
            let timezone = (self.timezone.0 as i32).abs();
            write!(f, "{sign}{:02}:{:02}", timezone / 60, timezone % 60)?;
        }
        Ok(())
    }
}

/// Value Definition for EFI_TIME.TimeZone
/// from UEFI spec 8.3 - Time Services
pub const EFI_UNSPECIFIED_TIMEZONE: EfiTimezone = EfiTimezone(0x07FF);

/// Timezone in minutes from UTC
///
/// Valid values include -1440 to 1440 or 2047 (EFI_UNSPECIFIED_TIMEZONE)
#[derive(Copy, Clone, Debug, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
#[repr(transparent)]
#[cfg_attr(feature = "inspect", derive(inspect::Inspect), inspect(transparent))]
pub struct EfiTimezone(pub i16);

impl EfiTimezone {
    pub fn valid(&self) -> bool {
        self.0 > -1440 && (self.0 < 1440 || *self == EFI_UNSPECIFIED_TIMEZONE)
    }
}

/// Bit Definitions for EFI_TIME.EfiDaylight
/// from UEFI spec 8.3 - Time Services
#[bitfield(u8)]
#[derive(AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
#[cfg_attr(feature = "inspect", derive(inspect::Inspect), inspect(transparent))]
pub struct EfiDaylight {
    /// EFI_TIME_ADJUST_DAYLIGHT
    ///
    /// the time is affected by daylight savings time
    pub adjust_daylight: bool,

    /// EFI_TIME_IN_DAYLIGHT
    ///
    /// the time has been adjusted for daylight savings time
    pub in_daylight: bool,

    #[bits(6)]
    rsvd: u8,
}

impl EfiDaylight {
    pub fn valid(&self) -> bool {
        self.rsvd() == 0
    }
}