1use core::fmt::{self, Display, Formatter};
8use core::time::Duration;
9use shared::decompose_unix_timestamp;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37#[repr(transparent)]
38pub struct Time {
39 seconds: u64,
40}
41
42impl Time {
43 pub const fn new(seconds: u64) -> Self {
58 Self { seconds }
59 }
60
61 pub const fn as_u64(self) -> u64 {
77 self.seconds
78 }
79}
80
81impl From<Duration> for Time {
85 fn from(duration: Duration) -> Self {
86 Self {
87 seconds: duration.as_secs(),
88 }
89 }
90}
91
92impl From<Time> for Duration {
96 fn from(time: Time) -> Self {
97 Duration::new(time.seconds, 0)
98 }
99}
100
101impl Display for Time {
102 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
103 let (year, month, day, hour, minute, second) =
104 decompose_unix_timestamp(self.seconds as i64);
105
106 write!(
107 f,
108 "{year:04}-{month:02}-{day:02} {hour:02}:{minute:02}:{second:02}",
109 )
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use alloc::{format, vec};
117
118 #[test]
119 fn test_time_creation() {
120 let time = Time::new(1640995200); assert_eq!(time.as_u64(), 1640995200);
122 }
123
124 #[test]
125 fn test_time_epoch() {
126 let epoch = Time::new(0);
127 assert_eq!(epoch.as_u64(), 0);
128 }
129
130 #[test]
131 fn test_time_const_operations() {
132 const TIME: Time = Time::new(1234567890);
134 const SECONDS: u64 = TIME.as_u64();
135
136 assert_eq!(SECONDS, 1234567890);
137 assert_eq!(TIME.as_u64(), 1234567890);
138 }
139
140 #[test]
141 fn test_time_comparison() {
142 let early = Time::new(1000);
143 let late = Time::new(2000);
144
145 assert!(early < late);
146 assert!(late > early);
147 assert!(early <= late);
148 assert!(late >= early);
149 assert!(early <= early);
150 assert!(late >= late);
151 assert_eq!(early, early);
152 assert_ne!(early, late);
153 }
154
155 #[test]
156 fn test_time_ordering() {
157 let mut times = [
158 Time::new(3000),
159 Time::new(1000),
160 Time::new(2000),
161 Time::new(500),
162 ];
163
164 times.sort();
165
166 assert_eq!(times[0], Time::new(500));
167 assert_eq!(times[1], Time::new(1000));
168 assert_eq!(times[2], Time::new(2000));
169 assert_eq!(times[3], Time::new(3000));
170 }
171
172 #[test]
173 fn test_time_clone_copy() {
174 let original = Time::new(999);
175 let cloned = original;
176 let copied = original;
177
178 assert_eq!(original, cloned);
179 assert_eq!(original, copied);
180 assert_eq!(cloned, copied);
181
182 assert_eq!(original.as_u64(), 999);
184 }
185
186 #[test]
187 fn test_time_debug() {
188 let time = Time::new(1640995200);
189 let debug_str = format!("{time:?}");
190 assert!(debug_str.contains("Time"));
191 assert!(debug_str.contains("1640995200"));
192 }
193
194 #[test]
195 fn test_time_hash() {
196 use alloc::collections::BTreeMap;
197
198 let time1 = Time::new(12345);
199 let time2 = Time::new(12345);
200 let time3 = Time::new(54321);
201
202 let mut map = BTreeMap::new();
204 map.insert(time1, "first");
205 map.insert(time2, "second"); map.insert(time3, "third");
207
208 assert_eq!(map.len(), 2); assert_eq!(map.get(&time1), Some(&"second"));
210 assert_eq!(map.get(&time3), Some(&"third"));
211 }
212
213 #[test]
214 fn test_time_from_duration() {
215 let duration = Duration::new(1640995200, 0);
216 let time: Time = duration.into();
217 assert_eq!(time.as_u64(), 1640995200);
218 }
219
220 #[test]
221 fn test_time_to_duration() {
222 let time = Time::new(1640995200);
223 let duration: Duration = time.into();
224 assert_eq!(duration.as_secs(), 1640995200);
225 }
226
227 #[test]
228 fn test_time_display_formatting() {
229 let time = Time::new(0); let display_str = format!("{time}");
232
233 assert!(display_str.contains("-"));
236 assert!(display_str.contains(":"));
237 assert!(display_str.len() > 10); }
239
240 #[test]
241 fn test_time_display_various_dates() {
242 let times = vec![
244 Time::new(0), Time::new(86400), Time::new(1640995200), ];
248
249 for time in times {
250 let display_str = format!("{time}");
251 assert!(display_str.len() >= 19); assert!(display_str.contains("-"));
254 assert!(display_str.contains(":"));
255 assert!(display_str.contains(" "));
256 }
257 }
258
259 #[test]
260 fn test_time_max_value() {
261 let max_time = Time::new(u64::MAX);
262 assert_eq!(max_time.as_u64(), u64::MAX);
263
264 let duration: Duration = max_time.into();
266 assert_eq!(duration.as_secs(), u64::MAX);
267 }
268
269 #[test]
270 fn test_time_zero_and_max_comparison() {
271 let zero = Time::new(0);
272 let max = Time::new(u64::MAX);
273
274 assert!(zero < max);
275 assert!(max > zero);
276 assert_ne!(zero, max);
277 }
278
279 #[test]
280 fn test_time_round_trip_conversions() {
281 let original_seconds = 1640995200u64;
282
283 let time = Time::new(original_seconds);
285 let duration: Duration = time.into();
286 let back_to_time: Time = duration.into();
287
288 assert_eq!(time, back_to_time);
289 assert_eq!(original_seconds, back_to_time.as_u64());
290 }
291
292 #[test]
293 fn test_time_type_size() {
294 use core::mem::{align_of, size_of};
295
296 assert_eq!(size_of::<Time>(), size_of::<u64>());
298 assert_eq!(align_of::<Time>(), align_of::<u64>());
299 }
300
301 #[test]
302 fn test_time_sequence() {
303 use alloc::vec::Vec;
305 let times: Vec<Time> = (0..10)
306 .map(|i| Time::new(i * 86400)) .collect();
308
309 for i in 1..times.len() {
311 assert!(times[i - 1] < times[i]);
312 }
313 }
314}