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