1use bitflags::bitflags;
6use core::fmt::{self, Display, Formatter};
7
8#[derive(Debug, Default, Copy, Clone, Eq)]
10#[repr(C)]
11pub struct Time {
12 pub year: u16,
14
15 pub month: u8,
17
18 pub day: u8,
20
21 pub hour: u8,
23
24 pub minute: u8,
26
27 pub second: u8,
29
30 pub pad1: u8,
32
33 pub nanosecond: u32,
35
36 pub time_zone: i16,
39
40 pub daylight: Daylight,
42
43 pub pad2: u8,
45}
46
47impl Time {
48 pub const UNSPECIFIED_TIMEZONE: i16 = 0x07ff;
50
51 #[must_use]
53 pub const fn invalid() -> Self {
54 Self {
55 year: 0,
56 month: 0,
57 day: 0,
58 hour: 0,
59 minute: 0,
60 second: 0,
61 pad1: 0,
62 nanosecond: 0,
63 time_zone: 0,
64 daylight: Daylight::empty(),
65 pad2: 0,
66 }
67 }
68
69 #[must_use]
71 pub fn is_valid(&self) -> bool {
72 (1900..=9999).contains(&self.year)
73 && (1..=12).contains(&self.month)
74 && (1..=31).contains(&self.day)
75 && self.hour <= 23
76 && self.minute <= 59
77 && self.second <= 59
78 && self.nanosecond <= 999_999_999
79 && ((-1440..=1440).contains(&self.time_zone)
80 || self.time_zone == Self::UNSPECIFIED_TIMEZONE)
81 }
82}
83
84impl Display for Time {
85 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
86 write!(f, "{:04}-{:02}-{:02} ", self.year, self.month, self.day)?;
87 write!(
88 f,
89 "{:02}:{:02}:{:02}.{:09}",
90 self.hour, self.minute, self.second, self.nanosecond
91 )?;
92
93 if self.time_zone == Self::UNSPECIFIED_TIMEZONE {
94 write!(f, " (local)")?;
95 } else {
96 let offset_in_hours = self.time_zone as f32 / 60.0;
97 let integer_part = offset_in_hours as i16;
98 let fraction_part = offset_in_hours - (integer_part as f32);
100 if fraction_part == 0.0 {
102 write!(f, "UTC+{offset_in_hours}")?;
103 }
104 else {
106 write!(f, "UTC+{offset_in_hours:.1}")?;
107 }
108 }
109
110 Ok(())
111 }
112}
113
114impl PartialEq for Time {
116 fn eq(&self, other: &Self) -> bool {
117 self.year == other.year
118 && self.month == other.month
119 && self.day == other.day
120 && self.hour == other.hour
121 && self.minute == other.minute
122 && self.second == other.second
123 && self.nanosecond == other.nanosecond
124 && self.time_zone == other.time_zone
125 && self.daylight == other.daylight
126 }
127}
128
129bitflags! {
130 #[repr(transparent)]
132 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
133 pub struct Daylight: u8 {
134 const ADJUST_DAYLIGHT = 0x01;
136
137 const IN_DAYLIGHT = 0x02;
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 extern crate alloc;
145
146 use super::*;
147 use alloc::string::ToString;
148
149 #[test]
150 fn test_time_display() {
151 let mut time = Time {
152 year: 2023,
153 month: 5,
154 day: 18,
155 hour: 11,
156 minute: 29,
157 second: 57,
158 nanosecond: 123_456_789,
159 time_zone: Time::UNSPECIFIED_TIMEZONE,
160 daylight: Daylight::empty(),
161 pad1: 0,
162 pad2: 0,
163 };
164 assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789 (local)");
165
166 time.time_zone = 120;
167 assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2");
168
169 time.time_zone = 150;
170 assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2.5");
171 }
172}