1use std::fmt::{Display, Formatter};
5
6use serde::{
7 Deserialize, Deserializer, Serialize, Serializer,
8 de::{self, Visitor},
9};
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub struct Time {
17 nanos_since_midnight: u64,
19}
20
21impl Default for Time {
22 fn default() -> Self {
23 Self {
24 nanos_since_midnight: 0,
25 } }
27}
28
29impl Time {
30 const MAX_NANOS_IN_DAY: u64 = 86_399_999_999_999;
32 const NANOS_PER_SECOND: u64 = 1_000_000_000;
33 const NANOS_PER_MINSVTE: u64 = 60 * Self::NANOS_PER_SECOND;
34 const NANOS_PER_HOUR: u64 = 60 * Self::NANOS_PER_MINSVTE;
35
36 pub fn new(hour: u32, min: u32, sec: u32, nano: u32) -> Option<Self> {
37 if hour >= 24 || min >= 60 || sec >= 60 || nano >= Self::NANOS_PER_SECOND as u32 {
39 return None;
40 }
41
42 let nanos = hour as u64 * Self::NANOS_PER_HOUR
43 + min as u64 * Self::NANOS_PER_MINSVTE
44 + sec as u64 * Self::NANOS_PER_SECOND
45 + nano as u64;
46
47 Some(Self {
48 nanos_since_midnight: nanos,
49 })
50 }
51
52 pub fn from_hms(hour: u32, min: u32, sec: u32) -> Result<Self, String> {
53 Self::new(hour, min, sec, 0).ok_or_else(|| format!("Invalid time: {:02}:{:02}:{:02}", hour, min, sec))
54 }
55
56 pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> Result<Self, String> {
57 Self::new(hour, min, sec, nano)
58 .ok_or_else(|| format!("Invalid time: {:02}:{:02}:{:02}.{:09}", hour, min, sec, nano))
59 }
60
61 pub fn midnight() -> Self {
62 Self {
63 nanos_since_midnight: 0,
64 }
65 }
66
67 pub fn noon() -> Self {
68 Self {
69 nanos_since_midnight: 12 * Self::NANOS_PER_HOUR,
70 }
71 }
72
73 pub fn hour(&self) -> u32 {
74 (self.nanos_since_midnight / Self::NANOS_PER_HOUR) as u32
75 }
76
77 pub fn minute(&self) -> u32 {
78 ((self.nanos_since_midnight % Self::NANOS_PER_HOUR) / Self::NANOS_PER_MINSVTE) as u32
79 }
80
81 pub fn second(&self) -> u32 {
82 ((self.nanos_since_midnight % Self::NANOS_PER_MINSVTE) / Self::NANOS_PER_SECOND) as u32
83 }
84
85 pub fn nanosecond(&self) -> u32 {
86 (self.nanos_since_midnight % Self::NANOS_PER_SECOND) as u32
87 }
88
89 pub fn to_nanos_since_midnight(&self) -> u64 {
91 self.nanos_since_midnight
92 }
93
94 pub fn from_nanos_since_midnight(nanos: u64) -> Option<Self> {
96 if nanos > Self::MAX_NANOS_IN_DAY {
97 return None;
98 }
99 Some(Self {
100 nanos_since_midnight: nanos,
101 })
102 }
103}
104
105impl Display for Time {
106 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107 let hours = self.hour();
108 let minutes = self.minute();
109 let seconds = self.second();
110 let nanos = self.nanosecond();
111
112 write!(f, "{:02}:{:02}:{:02}.{:09}", hours, minutes, seconds, nanos)
113 }
114}
115
116impl Serialize for Time {
118 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119 where
120 S: Serializer,
121 {
122 serializer.serialize_str(&self.to_string())
123 }
124}
125
126struct TimeVisitor;
127
128impl<'de> Visitor<'de> for TimeVisitor {
129 type Value = Time;
130
131 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
132 formatter.write_str("a time in ISO 8601 format (HH:MM:SS or HH:MM:SS.nnnnnnnnn)")
133 }
134
135 fn visit_str<E>(self, value: &str) -> Result<Time, E>
136 where
137 E: de::Error,
138 {
139 let (time_part, nano_part) = if let Some(dot_pos) = value.find('.') {
141 (&value[..dot_pos], Some(&value[dot_pos + 1..]))
142 } else {
143 (value, None)
144 };
145
146 let time_parts: Vec<&str> = time_part.split(':').collect();
147 if time_parts.len() != 3 {
148 return Err(E::custom(format!("invalid time format: {}", value)));
149 }
150
151 let hour = time_parts[0]
152 .parse::<u32>()
153 .map_err(|_| E::custom(format!("invalid hour: {}", time_parts[0])))?;
154 let minute = time_parts[1]
155 .parse::<u32>()
156 .map_err(|_| E::custom(format!("invalid minute: {}", time_parts[1])))?;
157 let second = time_parts[2]
158 .parse::<u32>()
159 .map_err(|_| E::custom(format!("invalid second: {}", time_parts[2])))?;
160
161 let nano = if let Some(nano_str) = nano_part {
162 let padded = if nano_str.len() < 9 {
164 format!("{:0<9}", nano_str)
165 } else {
166 nano_str[..9].to_string()
167 };
168 padded.parse::<u32>().map_err(|_| E::custom(format!("invalid nanoseconds: {}", nano_str)))?
169 } else {
170 0
171 };
172
173 Time::new(hour, minute, second, nano).ok_or_else(|| {
174 E::custom(format!("invalid time: {:02}:{:02}:{:02}.{:09}", hour, minute, second, nano))
175 })
176 }
177}
178
179impl<'de> Deserialize<'de> for Time {
180 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181 where
182 D: Deserializer<'de>,
183 {
184 deserializer.deserialize_str(TimeVisitor)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_time_display_standard_format() {
194 let time = Time::new(14, 30, 45, 123456789).unwrap();
195 assert_eq!(format!("{}", time), "14:30:45.123456789");
196
197 let time = Time::new(0, 0, 0, 0).unwrap();
198 assert_eq!(format!("{}", time), "00:00:00.000000000");
199
200 let time = Time::new(23, 59, 59, 999999999).unwrap();
201 assert_eq!(format!("{}", time), "23:59:59.999999999");
202 }
203
204 #[test]
205 fn test_time_display_millisecond_precision() {
206 let time = Time::new(14, 30, 45, 123000000).unwrap();
208 assert_eq!(format!("{}", time), "14:30:45.123000000");
209
210 let time = Time::new(14, 30, 45, 001000000).unwrap();
211 assert_eq!(format!("{}", time), "14:30:45.001000000");
212
213 let time = Time::new(14, 30, 45, 999000000).unwrap();
214 assert_eq!(format!("{}", time), "14:30:45.999000000");
215 }
216
217 #[test]
218 fn test_time_display_microsecond_precision() {
219 let time = Time::new(14, 30, 45, 123456000).unwrap();
221 assert_eq!(format!("{}", time), "14:30:45.123456000");
222
223 let time = Time::new(14, 30, 45, 000001000).unwrap();
224 assert_eq!(format!("{}", time), "14:30:45.000001000");
225
226 let time = Time::new(14, 30, 45, 999999000).unwrap();
227 assert_eq!(format!("{}", time), "14:30:45.999999000");
228 }
229
230 #[test]
231 fn test_time_display_nanosecond_precision() {
232 let time = Time::new(14, 30, 45, 123456789).unwrap();
234 assert_eq!(format!("{}", time), "14:30:45.123456789");
235
236 let time = Time::new(14, 30, 45, 000000001).unwrap();
237 assert_eq!(format!("{}", time), "14:30:45.000000001");
238
239 let time = Time::new(14, 30, 45, 999999999).unwrap();
240 assert_eq!(format!("{}", time), "14:30:45.999999999");
241 }
242
243 #[test]
244 fn test_time_display_zero_fractional_seconds() {
245 let time = Time::new(14, 30, 45, 0).unwrap();
246 assert_eq!(format!("{}", time), "14:30:45.000000000");
247
248 let time = Time::new(0, 0, 0, 0).unwrap();
249 assert_eq!(format!("{}", time), "00:00:00.000000000");
250 }
251
252 #[test]
253 fn test_time_display_edge_times() {
254 let time = Time::new(0, 0, 0, 0).unwrap();
256 assert_eq!(format!("{}", time), "00:00:00.000000000");
257
258 let time = Time::new(23, 59, 59, 999999999).unwrap();
260 assert_eq!(format!("{}", time), "23:59:59.999999999");
261
262 let time = Time::new(12, 0, 0, 0).unwrap();
264 assert_eq!(format!("{}", time), "12:00:00.000000000");
265
266 let time = Time::new(23, 59, 58, 999999999).unwrap();
268 assert_eq!(format!("{}", time), "23:59:58.999999999");
269
270 let time = Time::new(0, 0, 1, 0).unwrap();
272 assert_eq!(format!("{}", time), "00:00:01.000000000");
273 }
274
275 #[test]
276 fn test_time_display_special_times() {
277 let midnight = Time::midnight();
279 assert_eq!(format!("{}", midnight), "00:00:00.000000000");
280
281 let noon = Time::noon();
282 assert_eq!(format!("{}", noon), "12:00:00.000000000");
283
284 let default = Time::default();
286 assert_eq!(format!("{}", default), "00:00:00.000000000");
287 }
288
289 #[test]
290 fn test_time_display_all_hours() {
291 for hour in 0..24 {
292 let time = Time::new(hour, 30, 45, 123456789).unwrap();
293 let expected = format!("{:02}:30:45.123456789", hour);
294 assert_eq!(format!("{}", time), expected);
295 }
296 }
297
298 #[test]
299 fn test_time_display_all_minutes() {
300 for minute in 0..60 {
301 let time = Time::new(14, minute, 45, 123456789).unwrap();
302 let expected = format!("14:{:02}:45.123456789", minute);
303 assert_eq!(format!("{}", time), expected);
304 }
305 }
306
307 #[test]
308 fn test_time_display_all_seconds() {
309 for second in 0..60 {
310 let time = Time::new(14, 30, second, 123456789).unwrap();
311 let expected = format!("14:30:{:02}.123456789", second);
312 assert_eq!(format!("{}", time), expected);
313 }
314 }
315
316 #[test]
317 fn test_time_display_from_hms() {
318 let time = Time::from_hms(14, 30, 45).unwrap();
319 assert_eq!(format!("{}", time), "14:30:45.000000000");
320
321 let time = Time::from_hms(0, 0, 0).unwrap();
322 assert_eq!(format!("{}", time), "00:00:00.000000000");
323
324 let time = Time::from_hms(23, 59, 59).unwrap();
325 assert_eq!(format!("{}", time), "23:59:59.000000000");
326 }
327
328 #[test]
329 fn test_time_display_from_hms_nano() {
330 let time = Time::from_hms_nano(14, 30, 45, 123456789).unwrap();
331 assert_eq!(format!("{}", time), "14:30:45.123456789");
332
333 let time = Time::from_hms_nano(0, 0, 0, 0).unwrap();
334 assert_eq!(format!("{}", time), "00:00:00.000000000");
335
336 let time = Time::from_hms_nano(23, 59, 59, 999999999).unwrap();
337 assert_eq!(format!("{}", time), "23:59:59.999999999");
338 }
339
340 #[test]
341 fn test_time_display_from_nanos_since_midnight() {
342 let time = Time::from_nanos_since_midnight(0).unwrap();
344 assert_eq!(format!("{}", time), "00:00:00.000000000");
345
346 let time = Time::from_nanos_since_midnight(1_000_000_000).unwrap();
348 assert_eq!(format!("{}", time), "00:00:01.000000000");
349
350 let time = Time::from_nanos_since_midnight(60_000_000_000).unwrap();
352 assert_eq!(format!("{}", time), "00:01:00.000000000");
353
354 let time = Time::from_nanos_since_midnight(3_600_000_000_000).unwrap();
356 assert_eq!(format!("{}", time), "01:00:00.000000000");
357
358 let nanos = 14 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000 + 45 * 1_000_000_000 + 123456789;
360 let time = Time::from_nanos_since_midnight(nanos).unwrap();
361 assert_eq!(format!("{}", time), "14:30:45.123456789");
362 }
363
364 #[test]
365 fn test_time_display_boundary_values() {
366 let nanos = 24 * 3600 * 1_000_000_000 - 1;
368 let time = Time::from_nanos_since_midnight(nanos).unwrap();
369 assert_eq!(format!("{}", time), "23:59:59.999999999");
370
371 let time = Time::from_nanos_since_midnight(1).unwrap();
373 assert_eq!(format!("{}", time), "00:00:00.000000001");
374 }
375
376 #[test]
377 fn test_time_display_precision_patterns() {
378 let time = Time::new(14, 30, 45, 100000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.100000000");
381
382 let time = Time::new(14, 30, 45, 010000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.010000000");
384
385 let time = Time::new(14, 30, 45, 001000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.001000000");
387
388 let time = Time::new(14, 30, 45, 000100000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000100000");
390
391 let time = Time::new(14, 30, 45, 000010000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000010000");
393
394 let time = Time::new(14, 30, 45, 000001000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000001000");
396
397 let time = Time::new(14, 30, 45, 000000100).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000100");
399
400 let time = Time::new(14, 30, 45, 000000010).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000010");
402
403 let time = Time::new(14, 30, 45, 000000001).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000001");
405 }
406
407 #[test]
408 fn test_invalid_times() {
409 assert!(Time::new(24, 0, 0, 0).is_none()); assert!(Time::new(0, 60, 0, 0).is_none()); assert!(Time::new(0, 0, 60, 0).is_none()); assert!(Time::new(0, 0, 0, 1_000_000_000).is_none()); }
414
415 #[test]
416 fn test_time_roundtrip() {
417 let test_times = [(0, 0, 0, 0), (12, 30, 45, 123456789), (23, 59, 59, 999999999)];
418
419 for (h, m, s, n) in test_times {
420 let time = Time::new(h, m, s, n).unwrap();
421 let nanos = time.to_nanos_since_midnight();
422 let recovered = Time::from_nanos_since_midnight(nanos).unwrap();
423
424 assert_eq!(time.hour(), recovered.hour());
425 assert_eq!(time.minute(), recovered.minute());
426 assert_eq!(time.second(), recovered.second());
427 assert_eq!(time.nanosecond(), recovered.nanosecond());
428 }
429 }
430
431 #[test]
432 fn test_serde_roundtrip() {
433 let time = Time::new(14, 30, 45, 123456789).unwrap();
434 let json = serde_json::to_string(&time).unwrap();
435 assert_eq!(json, "\"14:30:45.123456789\"");
436
437 let recovered: Time = serde_json::from_str(&json).unwrap();
438 assert_eq!(time, recovered);
439 }
440}