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 value::duration::Duration,
15};
16
17#[repr(transparent)]
18#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
19pub struct Time {
20 nanos_since_midnight: u64,
21}
22
23impl Time {
24 const MAX_NANOS_IN_DAY: u64 = 86_399_999_999_999;
25 const NANOS_PER_SECOND: u64 = 1_000_000_000;
26 const NANOS_PER_MINSVTE: u64 = 60 * Self::NANOS_PER_SECOND;
27 const NANOS_PER_HOUR: u64 = 60 * Self::NANOS_PER_MINSVTE;
28
29 fn overflow_err(message: impl Into<String>) -> TypeError {
30 TypeError::Temporal {
31 kind: TemporalKind::TimeOverflow {
32 message: message.into(),
33 },
34 message: "time overflow".to_string(),
35 fragment: Fragment::None,
36 }
37 }
38
39 pub fn new(hour: u32, min: u32, sec: u32, nano: u32) -> Option<Self> {
40 if hour >= 24 || min >= 60 || sec >= 60 || nano >= Self::NANOS_PER_SECOND as u32 {
41 return None;
42 }
43
44 let nanos = hour as u64 * Self::NANOS_PER_HOUR
45 + min as u64 * Self::NANOS_PER_MINSVTE
46 + sec as u64 * Self::NANOS_PER_SECOND
47 + nano as u64;
48
49 Some(Self {
50 nanos_since_midnight: nanos,
51 })
52 }
53
54 pub fn from_hms(hour: u32, min: u32, sec: u32) -> Result<Self, Box<TypeError>> {
55 Self::new(hour, min, sec, 0).ok_or_else(|| {
56 Box::new(Self::overflow_err(format!("invalid time: {:02}:{:02}:{:02}", hour, min, sec)))
57 })
58 }
59
60 pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> Result<Self, Box<TypeError>> {
61 Self::new(hour, min, sec, nano).ok_or_else(|| {
62 Box::new(Self::overflow_err(format!(
63 "invalid time: {:02}:{:02}:{:02}.{:09}",
64 hour, min, sec, nano
65 )))
66 })
67 }
68
69 pub fn midnight() -> Self {
70 Self {
71 nanos_since_midnight: 0,
72 }
73 }
74
75 pub fn noon() -> Self {
76 Self {
77 nanos_since_midnight: 12 * Self::NANOS_PER_HOUR,
78 }
79 }
80
81 pub fn hour(&self) -> u32 {
82 (self.nanos_since_midnight / Self::NANOS_PER_HOUR) as u32
83 }
84
85 pub fn minute(&self) -> u32 {
86 ((self.nanos_since_midnight % Self::NANOS_PER_HOUR) / Self::NANOS_PER_MINSVTE) as u32
87 }
88
89 pub fn second(&self) -> u32 {
90 ((self.nanos_since_midnight % Self::NANOS_PER_MINSVTE) / Self::NANOS_PER_SECOND) as u32
91 }
92
93 pub fn nanosecond(&self) -> u32 {
94 (self.nanos_since_midnight % Self::NANOS_PER_SECOND) as u32
95 }
96
97 pub fn to_nanos_since_midnight(&self) -> u64 {
98 self.nanos_since_midnight
99 }
100
101 pub fn from_nanos_since_midnight(nanos: u64) -> Option<Self> {
102 if nanos > Self::MAX_NANOS_IN_DAY {
103 return None;
104 }
105 Some(Self {
106 nanos_since_midnight: nanos,
107 })
108 }
109
110 pub fn saturating_add(self, rhs: Duration) -> Time {
111 let total = rhs.as_nanos().unwrap_or(if rhs.is_negative() {
112 i64::MIN
113 } else {
114 i64::MAX
115 });
116 let nanos =
117 (self.nanos_since_midnight as i128 + total as i128).clamp(0, Self::MAX_NANOS_IN_DAY as i128);
118 Self {
119 nanos_since_midnight: nanos as u64,
120 }
121 }
122
123 pub fn saturating_sub(self, rhs: Duration) -> Time {
124 let total = rhs.as_nanos().unwrap_or(if rhs.is_negative() {
125 i64::MIN
126 } else {
127 i64::MAX
128 });
129 let nanos =
130 (self.nanos_since_midnight as i128 - total as i128).clamp(0, Self::MAX_NANOS_IN_DAY as i128);
131 Self {
132 nanos_since_midnight: nanos as u64,
133 }
134 }
135}
136
137impl Display for Time {
138 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
139 let hours = self.hour();
140 let minutes = self.minute();
141 let seconds = self.second();
142 let nanos = self.nanosecond();
143
144 write!(f, "{:02}:{:02}:{:02}.{:09}", hours, minutes, seconds, nanos)
145 }
146}
147
148impl Serialize for Time {
149 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150 where
151 S: Serializer,
152 {
153 serializer.serialize_u64(self.nanos_since_midnight)
154 }
155}
156
157struct TimeVisitor;
158
159impl<'de> Visitor<'de> for TimeVisitor {
160 type Value = Time;
161
162 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
163 formatter.write_str("a time as nanoseconds since midnight (u64)")
164 }
165
166 fn visit_u64<E>(self, value: u64) -> Result<Time, E>
167 where
168 E: de::Error,
169 {
170 Time::from_nanos_since_midnight(value)
171 .ok_or_else(|| E::custom(format!("time nanoseconds out of range: {}", value)))
172 }
173}
174
175impl<'de> Deserialize<'de> for Time {
176 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177 where
178 D: Deserializer<'de>,
179 {
180 deserializer.deserialize_u64(TimeVisitor)
181 }
182}
183
184#[cfg(test)]
185pub mod tests {
186 use std::fmt::Debug;
187
188 use postcard::{from_bytes, to_allocvec};
189 use serde_json::{from_str, to_string};
190
191 use super::*;
192 use crate::error::{TemporalKind, TypeError};
193
194 #[test]
195 fn test_time_display_standard_format() {
196 let time = Time::new(14, 30, 45, 123456789).unwrap();
197 assert_eq!(format!("{}", time), "14:30:45.123456789");
198
199 let time = Time::new(0, 0, 0, 0).unwrap();
200 assert_eq!(format!("{}", time), "00:00:00.000000000");
201
202 let time = Time::new(23, 59, 59, 999999999).unwrap();
203 assert_eq!(format!("{}", time), "23:59:59.999999999");
204 }
205
206 #[test]
207 fn test_time_display_millisecond_precision() {
208 let time = Time::new(14, 30, 45, 123000000).unwrap();
209 assert_eq!(format!("{}", time), "14:30:45.123000000");
210
211 let time = Time::new(14, 30, 45, 001000000).unwrap();
212 assert_eq!(format!("{}", time), "14:30:45.001000000");
213
214 let time = Time::new(14, 30, 45, 999000000).unwrap();
215 assert_eq!(format!("{}", time), "14:30:45.999000000");
216 }
217
218 #[test]
219 fn test_time_display_microsecond_precision() {
220 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();
233 assert_eq!(format!("{}", time), "14:30:45.123456789");
234
235 let time = Time::new(14, 30, 45, 000000001).unwrap();
236 assert_eq!(format!("{}", time), "14:30:45.000000001");
237
238 let time = Time::new(14, 30, 45, 999999999).unwrap();
239 assert_eq!(format!("{}", time), "14:30:45.999999999");
240 }
241
242 #[test]
243 fn test_time_display_zero_fractional_seconds() {
244 let time = Time::new(14, 30, 45, 0).unwrap();
245 assert_eq!(format!("{}", time), "14:30:45.000000000");
246
247 let time = Time::new(0, 0, 0, 0).unwrap();
248 assert_eq!(format!("{}", time), "00:00:00.000000000");
249 }
250
251 #[test]
252 fn test_time_display_edge_times() {
253 let time = Time::new(0, 0, 0, 0).unwrap();
254 assert_eq!(format!("{}", time), "00:00:00.000000000");
255
256 let time = Time::new(23, 59, 59, 999999999).unwrap();
257 assert_eq!(format!("{}", time), "23:59:59.999999999");
258
259 let time = Time::new(12, 0, 0, 0).unwrap();
260 assert_eq!(format!("{}", time), "12:00:00.000000000");
261
262 let time = Time::new(23, 59, 58, 999999999).unwrap();
263 assert_eq!(format!("{}", time), "23:59:58.999999999");
264
265 let time = Time::new(0, 0, 1, 0).unwrap();
266 assert_eq!(format!("{}", time), "00:00:01.000000000");
267 }
268
269 #[test]
270 fn test_time_display_special_times() {
271 let midnight = Time::midnight();
272 assert_eq!(format!("{}", midnight), "00:00:00.000000000");
273
274 let noon = Time::noon();
275 assert_eq!(format!("{}", noon), "12:00:00.000000000");
276
277 let default = Time::default();
278 assert_eq!(format!("{}", default), "00:00:00.000000000");
279 }
280
281 #[test]
282 fn test_time_display_all_hours() {
283 for hour in 0..24 {
284 let time = Time::new(hour, 30, 45, 123456789).unwrap();
285 let expected = format!("{:02}:30:45.123456789", hour);
286 assert_eq!(format!("{}", time), expected);
287 }
288 }
289
290 #[test]
291 fn test_time_display_all_minutes() {
292 for minute in 0..60 {
293 let time = Time::new(14, minute, 45, 123456789).unwrap();
294 let expected = format!("14:{:02}:45.123456789", minute);
295 assert_eq!(format!("{}", time), expected);
296 }
297 }
298
299 #[test]
300 fn test_time_display_all_seconds() {
301 for second in 0..60 {
302 let time = Time::new(14, 30, second, 123456789).unwrap();
303 let expected = format!("14:30:{:02}.123456789", second);
304 assert_eq!(format!("{}", time), expected);
305 }
306 }
307
308 #[test]
309 fn test_time_display_from_hms() {
310 let time = Time::from_hms(14, 30, 45).unwrap();
311 assert_eq!(format!("{}", time), "14:30:45.000000000");
312
313 let time = Time::from_hms(0, 0, 0).unwrap();
314 assert_eq!(format!("{}", time), "00:00:00.000000000");
315
316 let time = Time::from_hms(23, 59, 59).unwrap();
317 assert_eq!(format!("{}", time), "23:59:59.000000000");
318 }
319
320 #[test]
321 fn test_time_display_from_hms_nano() {
322 let time = Time::from_hms_nano(14, 30, 45, 123456789).unwrap();
323 assert_eq!(format!("{}", time), "14:30:45.123456789");
324
325 let time = Time::from_hms_nano(0, 0, 0, 0).unwrap();
326 assert_eq!(format!("{}", time), "00:00:00.000000000");
327
328 let time = Time::from_hms_nano(23, 59, 59, 999999999).unwrap();
329 assert_eq!(format!("{}", time), "23:59:59.999999999");
330 }
331
332 #[test]
333 fn test_time_display_from_nanos_since_midnight() {
334 let time = Time::from_nanos_since_midnight(0).unwrap();
335 assert_eq!(format!("{}", time), "00:00:00.000000000");
336
337 let time = Time::from_nanos_since_midnight(1_000_000_000).unwrap();
338 assert_eq!(format!("{}", time), "00:00:01.000000000");
339
340 let time = Time::from_nanos_since_midnight(60_000_000_000).unwrap();
341 assert_eq!(format!("{}", time), "00:01:00.000000000");
342
343 let time = Time::from_nanos_since_midnight(3_600_000_000_000).unwrap();
344 assert_eq!(format!("{}", time), "01:00:00.000000000");
345
346 let nanos = 14 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000 + 45 * 1_000_000_000 + 123456789;
347 let time = Time::from_nanos_since_midnight(nanos).unwrap();
348 assert_eq!(format!("{}", time), "14:30:45.123456789");
349 }
350
351 #[test]
352 fn test_time_display_boundary_values() {
353 let nanos = 24 * 3600 * 1_000_000_000 - 1;
354 let time = Time::from_nanos_since_midnight(nanos).unwrap();
355 assert_eq!(format!("{}", time), "23:59:59.999999999");
356
357 let time = Time::from_nanos_since_midnight(1).unwrap();
358 assert_eq!(format!("{}", time), "00:00:00.000000001");
359 }
360
361 #[test]
362 fn test_time_display_precision_patterns() {
363 let time = Time::new(14, 30, 45, 100000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.100000000");
365
366 let time = Time::new(14, 30, 45, 010000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.010000000");
368
369 let time = Time::new(14, 30, 45, 001000000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.001000000");
371
372 let time = Time::new(14, 30, 45, 000100000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000100000");
374
375 let time = Time::new(14, 30, 45, 000010000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000010000");
377
378 let time = Time::new(14, 30, 45, 000001000).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000001000");
380
381 let time = Time::new(14, 30, 45, 000000100).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000100");
383
384 let time = Time::new(14, 30, 45, 000000010).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000010");
386
387 let time = Time::new(14, 30, 45, 000000001).unwrap(); assert_eq!(format!("{}", time), "14:30:45.000000001");
389 }
390
391 #[test]
392 fn test_invalid_times() {
393 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()); }
398
399 #[test]
400 fn test_time_roundtrip() {
401 let test_times = [(0, 0, 0, 0), (12, 30, 45, 123456789), (23, 59, 59, 999999999)];
402
403 for (h, m, s, n) in test_times {
404 let time = Time::new(h, m, s, n).unwrap();
405 let nanos = time.to_nanos_since_midnight();
406 let recovered = Time::from_nanos_since_midnight(nanos).unwrap();
407
408 assert_eq!(time.hour(), recovered.hour());
409 assert_eq!(time.minute(), recovered.minute());
410 assert_eq!(time.second(), recovered.second());
411 assert_eq!(time.nanosecond(), recovered.nanosecond());
412 }
413 }
414
415 #[test]
416 fn test_serde_roundtrip() {
417 let time = Time::new(14, 30, 45, 123456789).unwrap();
418 let json = to_string(&time).unwrap();
419 assert_eq!(json, time.to_nanos_since_midnight().to_string());
421
422 let recovered: Time = from_str(&json).unwrap();
423 assert_eq!(time, recovered);
424 }
425
426 #[test]
427 fn test_serde_postcard_roundtrip_preserves_all_fields() {
428 for (h, m, s, n) in [(0u32, 0u32, 0u32, 0u32), (14, 30, 45, 123456789), (23, 59, 59, 999999999)] {
430 let time = Time::new(h, m, s, n).unwrap();
431 let bytes = to_allocvec(&time).unwrap();
432 let recovered: Time = from_bytes(&bytes).unwrap();
433 assert_eq!(time, recovered);
434 assert_eq!(recovered.hour(), h);
435 assert_eq!(recovered.minute(), m);
436 assert_eq!(recovered.second(), s);
437 assert_eq!(recovered.nanosecond(), n);
438 }
439 }
440
441 #[test]
442 fn test_deserialize_rejects_out_of_range_nanos() {
443 let json = (Time::MAX_NANOS_IN_DAY + 1).to_string();
445 assert!(from_str::<Time>(&json).is_err());
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
481 #[test]
482 fn saturating_sub_before_midnight_clamps_to_midnight() {
483 let midnight = Time::midnight();
485 assert_eq!(midnight.saturating_sub(Duration::from_seconds(1).unwrap()), midnight);
486 }
487
488 #[test]
489 fn saturating_add_past_end_of_day_clamps_to_max() {
490 let late = Time::from_hms(23, 59, 59).unwrap();
492 let max = Time::from_nanos_since_midnight(86_399_999_999_999).unwrap();
493 assert_eq!(late.saturating_add(Duration::from_seconds(3600).unwrap()), max);
494 }
495
496 #[test]
497 fn saturating_add_sub_within_day() {
498 let t = Time::from_hms(10, 30, 0).unwrap();
499 assert_eq!(t.saturating_add(Duration::from_seconds(60).unwrap()), Time::from_hms(10, 31, 0).unwrap());
500 assert_eq!(t.saturating_sub(Duration::from_seconds(60).unwrap()), Time::from_hms(10, 29, 0).unwrap());
501 }
502}