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_u64(self.nanos_since_midnight)
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 as nanoseconds since midnight (u64)")
137 }
138
139 fn visit_u64<E>(self, value: u64) -> Result<Time, E>
140 where
141 E: de::Error,
142 {
143 Time::from_nanos_since_midnight(value)
144 .ok_or_else(|| E::custom(format!("time nanoseconds out of range: {}", value)))
145 }
146}
147
148impl<'de> Deserialize<'de> for Time {
149 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150 where
151 D: Deserializer<'de>,
152 {
153 deserializer.deserialize_u64(TimeVisitor)
154 }
155}
156
157#[cfg(test)]
158pub mod tests {
159 use std::fmt::Debug;
160
161 use postcard::{from_bytes, to_allocvec};
162 use serde_json::{from_str, to_string};
163
164 use super::*;
165 use crate::error::{TemporalKind, TypeError};
166
167 #[test]
168 fn test_time_display_standard_format() {
169 let time = Time::new(14, 30, 45, 123456789).unwrap();
170 assert_eq!(format!("{}", time), "14:30:45.123456789");
171
172 let time = Time::new(0, 0, 0, 0).unwrap();
173 assert_eq!(format!("{}", time), "00:00:00.000000000");
174
175 let time = Time::new(23, 59, 59, 999999999).unwrap();
176 assert_eq!(format!("{}", time), "23:59:59.999999999");
177 }
178
179 #[test]
180 fn test_time_display_millisecond_precision() {
181 let time = Time::new(14, 30, 45, 123000000).unwrap();
183 assert_eq!(format!("{}", time), "14:30:45.123000000");
184
185 let time = Time::new(14, 30, 45, 001000000).unwrap();
186 assert_eq!(format!("{}", time), "14:30:45.001000000");
187
188 let time = Time::new(14, 30, 45, 999000000).unwrap();
189 assert_eq!(format!("{}", time), "14:30:45.999000000");
190 }
191
192 #[test]
193 fn test_time_display_microsecond_precision() {
194 let time = Time::new(14, 30, 45, 123456000).unwrap();
196 assert_eq!(format!("{}", time), "14:30:45.123456000");
197
198 let time = Time::new(14, 30, 45, 000001000).unwrap();
199 assert_eq!(format!("{}", time), "14:30:45.000001000");
200
201 let time = Time::new(14, 30, 45, 999999000).unwrap();
202 assert_eq!(format!("{}", time), "14:30:45.999999000");
203 }
204
205 #[test]
206 fn test_time_display_nanosecond_precision() {
207 let time = Time::new(14, 30, 45, 123456789).unwrap();
209 assert_eq!(format!("{}", time), "14:30:45.123456789");
210
211 let time = Time::new(14, 30, 45, 000000001).unwrap();
212 assert_eq!(format!("{}", time), "14:30:45.000000001");
213
214 let time = Time::new(14, 30, 45, 999999999).unwrap();
215 assert_eq!(format!("{}", time), "14:30:45.999999999");
216 }
217
218 #[test]
219 fn test_time_display_zero_fractional_seconds() {
220 let time = Time::new(14, 30, 45, 0).unwrap();
221 assert_eq!(format!("{}", time), "14:30:45.000000000");
222
223 let time = Time::new(0, 0, 0, 0).unwrap();
224 assert_eq!(format!("{}", time), "00:00:00.000000000");
225 }
226
227 #[test]
228 fn test_time_display_edge_times() {
229 let time = Time::new(0, 0, 0, 0).unwrap();
231 assert_eq!(format!("{}", time), "00:00:00.000000000");
232
233 let time = Time::new(23, 59, 59, 999999999).unwrap();
235 assert_eq!(format!("{}", time), "23:59:59.999999999");
236
237 let time = Time::new(12, 0, 0, 0).unwrap();
239 assert_eq!(format!("{}", time), "12:00:00.000000000");
240
241 let time = Time::new(23, 59, 58, 999999999).unwrap();
243 assert_eq!(format!("{}", time), "23:59:58.999999999");
244
245 let time = Time::new(0, 0, 1, 0).unwrap();
247 assert_eq!(format!("{}", time), "00:00:01.000000000");
248 }
249
250 #[test]
251 fn test_time_display_special_times() {
252 let midnight = Time::midnight();
254 assert_eq!(format!("{}", midnight), "00:00:00.000000000");
255
256 let noon = Time::noon();
257 assert_eq!(format!("{}", noon), "12:00:00.000000000");
258
259 let default = Time::default();
261 assert_eq!(format!("{}", default), "00:00:00.000000000");
262 }
263
264 #[test]
265 fn test_time_display_all_hours() {
266 for hour in 0..24 {
267 let time = Time::new(hour, 30, 45, 123456789).unwrap();
268 let expected = format!("{:02}:30:45.123456789", hour);
269 assert_eq!(format!("{}", time), expected);
270 }
271 }
272
273 #[test]
274 fn test_time_display_all_minutes() {
275 for minute in 0..60 {
276 let time = Time::new(14, minute, 45, 123456789).unwrap();
277 let expected = format!("14:{:02}:45.123456789", minute);
278 assert_eq!(format!("{}", time), expected);
279 }
280 }
281
282 #[test]
283 fn test_time_display_all_seconds() {
284 for second in 0..60 {
285 let time = Time::new(14, 30, second, 123456789).unwrap();
286 let expected = format!("14:30:{:02}.123456789", second);
287 assert_eq!(format!("{}", time), expected);
288 }
289 }
290
291 #[test]
292 fn test_time_display_from_hms() {
293 let time = Time::from_hms(14, 30, 45).unwrap();
294 assert_eq!(format!("{}", time), "14:30:45.000000000");
295
296 let time = Time::from_hms(0, 0, 0).unwrap();
297 assert_eq!(format!("{}", time), "00:00:00.000000000");
298
299 let time = Time::from_hms(23, 59, 59).unwrap();
300 assert_eq!(format!("{}", time), "23:59:59.000000000");
301 }
302
303 #[test]
304 fn test_time_display_from_hms_nano() {
305 let time = Time::from_hms_nano(14, 30, 45, 123456789).unwrap();
306 assert_eq!(format!("{}", time), "14:30:45.123456789");
307
308 let time = Time::from_hms_nano(0, 0, 0, 0).unwrap();
309 assert_eq!(format!("{}", time), "00:00:00.000000000");
310
311 let time = Time::from_hms_nano(23, 59, 59, 999999999).unwrap();
312 assert_eq!(format!("{}", time), "23:59:59.999999999");
313 }
314
315 #[test]
316 fn test_time_display_from_nanos_since_midnight() {
317 let time = Time::from_nanos_since_midnight(0).unwrap();
319 assert_eq!(format!("{}", time), "00:00:00.000000000");
320
321 let time = Time::from_nanos_since_midnight(1_000_000_000).unwrap();
323 assert_eq!(format!("{}", time), "00:00:01.000000000");
324
325 let time = Time::from_nanos_since_midnight(60_000_000_000).unwrap();
327 assert_eq!(format!("{}", time), "00:01:00.000000000");
328
329 let time = Time::from_nanos_since_midnight(3_600_000_000_000).unwrap();
331 assert_eq!(format!("{}", time), "01:00:00.000000000");
332
333 let nanos = 14 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000 + 45 * 1_000_000_000 + 123456789;
335 let time = Time::from_nanos_since_midnight(nanos).unwrap();
336 assert_eq!(format!("{}", time), "14:30:45.123456789");
337 }
338
339 #[test]
340 fn test_time_display_boundary_values() {
341 let nanos = 24 * 3600 * 1_000_000_000 - 1;
343 let time = Time::from_nanos_since_midnight(nanos).unwrap();
344 assert_eq!(format!("{}", time), "23:59:59.999999999");
345
346 let time = Time::from_nanos_since_midnight(1).unwrap();
348 assert_eq!(format!("{}", time), "00:00:00.000000001");
349 }
350
351 #[test]
352 fn test_time_display_precision_patterns() {
353 let time = Time::new(14, 30, 45, 100000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.100000000");
356
357 let time = Time::new(14, 30, 45, 010000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.010000000");
359
360 let time = Time::new(14, 30, 45, 001000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.001000000");
362
363 let time = Time::new(14, 30, 45, 000100000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000100000");
365
366 let time = Time::new(14, 30, 45, 000010000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000010000");
368
369 let time = Time::new(14, 30, 45, 000001000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000001000");
371
372 let time = Time::new(14, 30, 45, 000000100).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000100");
374
375 let time = Time::new(14, 30, 45, 000000010).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000010");
377
378 let time = Time::new(14, 30, 45, 000000001).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000001");
380 }
381
382 #[test]
383 fn test_invalid_times() {
384 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()); }
389
390 #[test]
391 fn test_time_roundtrip() {
392 let test_times = [(0, 0, 0, 0), (12, 30, 45, 123456789), (23, 59, 59, 999999999)];
393
394 for (h, m, s, n) in test_times {
395 let time = Time::new(h, m, s, n).unwrap();
396 let nanos = time.to_nanos_since_midnight();
397 let recovered = Time::from_nanos_since_midnight(nanos).unwrap();
398
399 assert_eq!(time.hour(), recovered.hour());
400 assert_eq!(time.minute(), recovered.minute());
401 assert_eq!(time.second(), recovered.second());
402 assert_eq!(time.nanosecond(), recovered.nanosecond());
403 }
404 }
405
406 #[test]
407 fn test_serde_roundtrip() {
408 let time = Time::new(14, 30, 45, 123456789).unwrap();
409 let json = to_string(&time).unwrap();
410 assert_eq!(json, time.to_nanos_since_midnight().to_string());
412
413 let recovered: Time = from_str(&json).unwrap();
414 assert_eq!(time, recovered);
415 }
416
417 #[test]
418 fn test_serde_postcard_roundtrip_preserves_all_fields() {
419 for (h, m, s, n) in [(0u32, 0u32, 0u32, 0u32), (14, 30, 45, 123456789), (23, 59, 59, 999999999)] {
421 let time = Time::new(h, m, s, n).unwrap();
422 let bytes = to_allocvec(&time).unwrap();
423 let recovered: Time = from_bytes(&bytes).unwrap();
424 assert_eq!(time, recovered);
425 assert_eq!(recovered.hour(), h);
426 assert_eq!(recovered.minute(), m);
427 assert_eq!(recovered.second(), s);
428 assert_eq!(recovered.nanosecond(), n);
429 }
430 }
431
432 #[test]
433 fn test_deserialize_rejects_out_of_range_nanos() {
434 let json = (Time::MAX_NANOS_IN_DAY + 1).to_string();
436 assert!(from_str::<Time>(&json).is_err());
437 }
438
439 fn assert_time_overflow<T: Debug>(result: Result<T, Box<TypeError>>) {
440 let err = result.expect_err("expected TimeOverflow error");
441 match *err {
442 TypeError::Temporal {
443 kind: TemporalKind::TimeOverflow {
444 ..
445 },
446 ..
447 } => {}
448 other => panic!("expected TimeOverflow, got: {:?}", other),
449 }
450 }
451
452 #[test]
453 fn test_from_hms_invalid_hour() {
454 assert_time_overflow(Time::from_hms(24, 0, 0));
455 }
456
457 #[test]
458 fn test_from_hms_invalid_minute() {
459 assert_time_overflow(Time::from_hms(0, 60, 0));
460 }
461
462 #[test]
463 fn test_from_hms_invalid_second() {
464 assert_time_overflow(Time::from_hms(0, 0, 60));
465 }
466
467 #[test]
468 fn test_from_hms_nano_invalid_nano() {
469 assert_time_overflow(Time::from_hms_nano(0, 0, 0, 1_000_000_000));
470 }
471}