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