1use bitflags::bitflags;
4use core::fmt::{self, Display, Formatter};
5
6#[derive(Debug, Default, Copy, Clone, Eq)]
8#[repr(C)]
9pub struct Time {
10 pub year: u16,
12
13 pub month: u8,
15
16 pub day: u8,
18
19 pub hour: u8,
21
22 pub minute: u8,
24
25 pub second: u8,
27
28 pub pad1: u8,
30
31 pub nanosecond: u32,
33
34 pub time_zone: i16,
37
38 pub daylight: Daylight,
40
41 pub pad2: u8,
43}
44
45impl Time {
46 pub const UNSPECIFIED_TIMEZONE: i16 = 0x07ff;
48
49 #[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 #[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 let fraction_part = offset_in_hours - (integer_part as f32);
98 if fraction_part == 0.0 {
100 write!(f, "UTC+{offset_in_hours}")?;
101 }
102 else {
104 write!(f, "UTC+{offset_in_hours:.1}")?;
105 }
106 }
107
108 Ok(())
109 }
110}
111
112impl 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 #[repr(transparent)]
130 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
131 pub struct Daylight: u8 {
132 const ADJUST_DAYLIGHT = 0x01;
134
135 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}