1use crate::constants::{
2 MICROSECONDS_IN_SECOND, MINUTES_IN_HOUR, SECONDS_IN_HOUR, SECONDS_IN_MINUTE,
3};
4use crate::date_error::{DateError, DateErrorKind};
5use core::fmt;
6use std::cmp::{Ord, Ordering};
7use std::str::FromStr;
8use std::time::Duration;
9
10#[derive(Copy, Clone, Eq, PartialEq)]
11pub struct Time {
12 pub hour: u64,
13 pub minute: u64,
14 pub second: u64,
15 pub microsecond: u64,
16}
17
18#[cfg(feature = "serde")]
20impl serde::Serialize for Time {
21 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
22 where
23 S: serde::Serializer,
24 {
25 let microseconds = self.to_microseconds();
27 serializer.serialize_u64(microseconds)
28 }
29}
30
31#[cfg(feature = "serde")]
32impl<'de> serde::Deserialize<'de> for Time {
33 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
34 where
35 D: serde::Deserializer<'de>,
36 {
37 let microseconds = u64::deserialize(deserializer)?;
38 let seconds = microseconds / MICROSECONDS_IN_SECOND;
39 let micros = microseconds % MICROSECONDS_IN_SECOND;
40 let time = Time::from_seconds(seconds);
41 Ok(Time::new_with_microseconds(time.hour, time.minute, time.second, micros))
42 }
43}
44
45impl Time {
46 pub fn new(hour: u64, minute: u64, second: u64) -> Self {
47 Self::new_with_microseconds(hour, minute, second, 0)
48 }
49
50 pub fn new_with_microseconds(hour: u64, minute: u64, second: u64, microseconds: u64) -> Self {
51 Time {
52 hour,
53 minute,
54 second,
55 microsecond: microseconds,
56 }
57 }
58
59 pub fn valid(&self) -> bool {
60 self.hour < 24 && self.minute < 60 && self.second < 60 && self.microsecond < 1000000
61 }
62
63 pub fn to_hh_mm_string(&self) -> String {
64 format!("{:02}:{:02}", self.hour, self.minute)
65 }
66
67 pub fn from_ms_dos_time(mut ms_dos_time: u16) -> Self {
68 let second = (ms_dos_time & 0x1f) << 1;
69 ms_dos_time >>= 5;
70 let minute = ms_dos_time & 0x3f;
71 ms_dos_time >>= 6;
72 Time::new(ms_dos_time as u64, minute as u64, second as u64)
73 }
74
75 pub fn from_seconds(mut seconds: u64) -> Self {
76 let hour = seconds / SECONDS_IN_HOUR;
77 seconds %= SECONDS_IN_HOUR;
78 let minute = seconds / SECONDS_IN_MINUTE;
79 seconds %= SECONDS_IN_MINUTE;
80 Time::new(hour, minute, seconds)
81 }
82
83 pub fn to_seconds(&self) -> u64 {
84 self.to_minutes() * SECONDS_IN_MINUTE + self.second
85 }
86
87 pub fn to_minutes(&self) -> u64 {
88 self.hour * MINUTES_IN_HOUR + self.minute
89 }
90
91 pub fn to_microseconds(&self) -> u64 {
92 self.to_seconds() * MICROSECONDS_IN_SECOND + self.microsecond
93 }
94
95 pub fn to_milliseconds(&self) -> u64 {
96 self.to_microseconds() / 1000
97 }
98
99 pub fn from_minutes(mut minutes: u64) -> Self {
100 let hour = minutes / MINUTES_IN_HOUR;
101 minutes %= MINUTES_IN_HOUR;
102 Time::new(hour, minutes, 0)
103 }
104
105 pub fn from_hours(hour: u64) -> Self {
106 Time::new(hour, 0, 0)
107 }
108
109 pub fn from_duration(duration: Duration) -> Time {
110 Self::from_seconds(duration.as_secs())
111 }
112
113 pub fn to_duration(&self) -> Duration {
114 Duration::from_secs(self.to_seconds())
115 }
116
117 pub fn normalize(&self) -> Time {
118 let mut second = self.microsecond / MICROSECONDS_IN_SECOND + self.second;
119 let microseconds = self.microsecond % MICROSECONDS_IN_SECOND;
120 let mut minute = second / SECONDS_IN_MINUTE + self.minute;
121 second = second % SECONDS_IN_MINUTE;
122 let hour = minute / MINUTES_IN_HOUR + self.hour;
123 minute = minute % MINUTES_IN_HOUR;
124 Self::new_with_microseconds(hour, minute, second, microseconds)
125 }
126}
127
128impl FromStr for Time {
129 type Err = DateError;
130
131 fn from_str(time_str: &str) -> Result<Self, Self::Err> {
132 let bytes = time_str.as_bytes();
133 let len = bytes.len();
134
135 if len < 8 || bytes[2] != b':' || bytes[5] != b':' {
136 return Err(DateErrorKind::WrongTimeStringFormat.into());
137 }
138 let hour = parse_digits(&bytes[0..2])?;
139
140 let minute = parse_digits(&bytes[3..5])?;
141 let (second, microseconds) = if len > 8 && bytes[8] == b'.' {
142 let second = parse_digits(&bytes[6..8])?;
143 let micro_str = &bytes[9..];
144 let micro_len = micro_str.len().min(6);
145 let mut microseconds = parse_digits(µ_str[..micro_len])?;
146
147 for _ in micro_len..6 {
148 microseconds *= 10;
149 }
150
151 (second, microseconds)
152 } else {
153 let second = parse_digits(&bytes[6..8])?;
154 (second, 0)
155 };
156
157 Ok(Time::new_with_microseconds(
158 hour,
159 minute,
160 second,
161 microseconds,
162 ))
163 }
164}
165
166#[inline]
167fn parse_digits(bytes: &[u8]) -> Result<u64, DateError> {
168 let mut result = 0u64;
169 for &byte in bytes {
170 if byte < b'0' || byte > b'9' {
171 return Err(DateErrorKind::WrongTimeStringFormat.into());
172 }
173 result = result * 10 + (byte - b'0') as u64;
174 }
175 Ok(result)
176}
177
178impl fmt::Display for Time {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
181 if self.microsecond > 0 {
182 let mut divider = 1;
183 while self.microsecond % (divider * 10) == 0 {
184 divider *= 10;
185 }
186 let zeros = divider.ilog10();
187 let zeros = 6 - zeros as usize;
188 write!(f, ".{:0zeros$}", self.microsecond / divider)?;
189 }
190 Ok(())
191 }
192}
193
194impl fmt::Debug for Time {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 fmt::Display::fmt(self, f)
197 }
198}
199
200impl Ord for Time {
201 fn cmp(&self, other: &Self) -> Ordering {
202 if self.hour != other.hour {
203 return self.hour.cmp(&other.hour);
204 }
205 if self.minute != other.minute {
206 return self.minute.cmp(&other.minute);
207 }
208 if self.second != other.second {
209 return self.second.cmp(&other.second);
210 }
211 self.microsecond.cmp(&other.microsecond)
212 }
213}
214
215impl PartialOrd for Time {
216 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
217 Some(self.cmp(other))
218 }
219}
220
221impl std::ops::Sub for Time {
222 type Output = Time;
223
224 fn sub(self, rhs: Self) -> Self::Output {
225 let mut second = self.to_seconds() - rhs.to_seconds();
226 let mut microseconds = self.microsecond;
227 if self.microsecond < rhs.microsecond {
228 second -= 1;
229 microseconds += MICROSECONDS_IN_SECOND;
230 }
231 microseconds -= rhs.microsecond;
232 let mut result = Self::from_seconds(second);
233 result.microsecond = microseconds;
234 result
235 }
236}
237
238impl std::ops::Add for Time {
239 type Output = Time;
240
241 fn add(self, rhs: Self) -> Self::Output {
242 let mut total_microseconds = self.to_microseconds() + rhs.to_microseconds();
243
244 let microseconds = total_microseconds % MICROSECONDS_IN_SECOND;
245 total_microseconds /= MICROSECONDS_IN_SECOND;
246
247 let seconds = total_microseconds % SECONDS_IN_MINUTE;
248 total_microseconds /= SECONDS_IN_MINUTE;
249
250 let minutes = total_microseconds % MINUTES_IN_HOUR;
251 let hours = total_microseconds / MINUTES_IN_HOUR;
252
253 Time::new_with_microseconds(hours, minutes, seconds, microseconds)
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_ms_dos_time() {
263 assert_eq!(Time::from_ms_dos_time(0x7d1c), Time::new(15, 40, 56));
264 }
265
266 #[test]
267 fn test_time_cmp() {
268 assert!(Time::new(2, 12, 31) < Time::new(3, 1, 1));
269 assert!(Time::new(2, 2, 1) > Time::new(2, 1, 31));
270 assert!(Time::new(2, 3, 31) > Time::new(2, 3, 30));
271 assert_eq!(Time::new(2, 1, 1), Time::new(2, 1, 1));
272 assert!(
273 Time::new_with_microseconds(2, 3, 31, 11) > Time::new_with_microseconds(2, 3, 31, 10)
274 )
275 }
276
277 #[test]
278 fn test_from_str() -> Result<(), DateError> {
279 assert_eq!(
280 Time::from_str("21:10:05")?,
281 Time::new_with_microseconds(21, 10, 5, 0)
282 );
283 assert_eq!(
284 Time::from_str("21:10:05.779325")?,
285 Time::new_with_microseconds(21, 10, 5, 779325)
286 );
287 assert_eq!(
288 Time::from_str("21:10:05.77932599")?,
289 Time::new_with_microseconds(21, 10, 5, 779325)
290 );
291 assert_eq!(
292 Time::from_str("21:10:05.77932500")?,
293 Time::new_with_microseconds(21, 10, 5, 779325)
294 );
295 assert_eq!(
296 Time::from_str("21:10:05.779000")?,
297 Time::new_with_microseconds(21, 10, 5, 779000)
298 );
299 assert_eq!(
300 Time::from_str("21:10:05.779")?,
301 Time::new_with_microseconds(21, 10, 5, 779000)
302 );
303 assert_eq!(
304 Time::from_str("21:10:05.034104")?,
305 Time::new_with_microseconds(21, 10, 5, 034104)
306 );
307 Ok(())
308 }
309
310 #[test]
311 fn test_to_string() {
312 assert_eq!(
313 Time::new_with_microseconds(21, 10, 5, 0).to_string(),
314 "21:10:05"
315 );
316 assert_eq!(
317 Time::new_with_microseconds(21, 10, 5, 779325).to_string(),
318 "21:10:05.779325"
319 );
320 assert_eq!(
321 Time::new_with_microseconds(21, 10, 5, 779000).to_string(),
322 "21:10:05.779"
323 );
324 assert_eq!(
325 Time::new_with_microseconds(21, 10, 5, 779).to_string(),
326 "21:10:05.000779"
327 );
328 assert_eq!(
329 Time::new_with_microseconds(21, 10, 5, 34104).to_string(),
330 "21:10:05.034104"
331 );
332 }
333
334 #[test]
335 fn test_time_to_minutes() {
336 assert_eq!(Time::new_with_microseconds(2, 3, 31, 11).to_minutes(), 123);
337 }
338
339 #[test]
340 fn test_time_to_seconds() {
341 assert_eq!(Time::new_with_microseconds(2, 3, 31, 11).to_seconds(), 7411);
342 }
343
344 #[test]
345 fn test_time_to_microseconds() {
346 assert_eq!(
347 Time::new_with_microseconds(2, 3, 31, 11).to_microseconds(),
348 7411000011
349 );
350 assert_eq!(
351 Time::new_with_microseconds(2, 3, 31, 23560).to_microseconds(),
352 7411023560
353 );
354 }
355
356 #[test]
357 fn test_time_to_milliseconds() {
358 assert_eq!(
359 Time::new_with_microseconds(2, 3, 31, 23560).to_milliseconds(),
360 7411023
361 );
362 }
363
364 #[test]
365 fn test_time_normalize() {
366 assert_eq!(
367 Time::new_with_microseconds(42, 81, 74, 76543213).normalize(),
368 Time::new_with_microseconds(43, 23, 30, 543213)
369 );
370 }
371
372 #[test]
373 fn test_time_add() {
374 assert_eq!(
375 Time::new_with_microseconds(100, 59, 59, 999999)
376 + Time::new_with_microseconds(0, 0, 1, 1),
377 Time::new(101, 0, 1)
378 );
379 }
380
381 #[test]
382 fn test_time_sub() {
383 assert_eq!(
384 Time::new_with_microseconds(100, 0, 0, 0) - Time::new_with_microseconds(0, 0, 0, 1),
385 Time::new_with_microseconds(99, 59, 59, 999999)
386 );
387 }
388
389 #[test]
390 fn test_time_validation() {
391 assert!(Time::new(0, 0, 0).valid());
392 assert!(Time::new(23, 59, 59).valid());
393 assert!(Time::new(12, 30, 45).valid());
394 assert!(!Time::new(24, 0, 0).valid()); assert!(!Time::new(25, 0, 0).valid()); assert!(!Time::new(12, 60, 0).valid()); assert!(!Time::new(12, 0, 60).valid()); assert!(!Time::new(12, 61, 0).valid()); assert!(!Time::new(12, 0, 61).valid()); }
401
402 #[test]
403 fn test_time_from_str_invalid() {
404 assert!("invalid".parse::<Time>().is_err());
405 assert!("12:00".parse::<Time>().is_err());
406 assert!("12".parse::<Time>().is_err());
407 assert!("12-00-00".parse::<Time>().is_err());
408 }
409
410 #[test]
411 fn test_time_conversions() {
412 let time = Time::new(2, 30, 45);
413
414 assert_eq!(time.to_minutes(), 150); assert_eq!(time.to_seconds(), 9045); assert_eq!(time.to_milliseconds(), 9045000);
417
418 assert_eq!(Time::from_minutes(150), Time::new(2, 30, 0));
419 assert_eq!(Time::from_hours(2), Time::new(2, 0, 0));
420 assert_eq!(Time::from_seconds(9045), Time::new(2, 30, 45));
421 }
422
423 #[test]
424 fn test_time_with_microseconds() {
425 let time = Time::new_with_microseconds(12, 30, 45, 123456);
426
427 assert_eq!(time.to_microseconds(), 45045123456);
428 assert_eq!(time.to_milliseconds(), 45045123);
429
430 assert_eq!(time.to_string(), "12:30:45.123456");
431 assert_eq!("12:30:45.123456".parse::<Time>().unwrap(), time);
432 }
433
434 #[test]
435 fn test_time_edge_cases() {
436 let midnight = Time::new(0, 0, 0);
437 assert_eq!(midnight.to_string(), "00:00:00");
438 assert_eq!(midnight.to_seconds(), 0);
439
440 let end_of_day = Time::new(23, 59, 59);
441 assert_eq!(end_of_day.to_string(), "23:59:59");
442 assert_eq!(end_of_day.to_seconds(), 86399);
443
444 let time_with_micros = Time::new_with_microseconds(0, 0, 0, 999999);
445 assert_eq!(time_with_micros.to_string(), "00:00:00.999999");
446 }
447
448 #[test]
449 fn test_time_duration_conversion() {
450 let time = Time::new(1, 30, 45);
451 let duration = time.to_duration();
452 assert_eq!(duration.as_secs(), 5445);
453
454 let from_duration = Time::from_duration(duration);
455 assert_eq!(from_duration, time);
456 }
457
458 #[test]
459 fn test_time_hh_mm_string() {
460 let time = Time::new(9, 5, 30);
461 assert_eq!(time.to_hh_mm_string(), "09:05");
462
463 let time2 = Time::new(23, 59, 0);
464 assert_eq!(time2.to_hh_mm_string(), "23:59");
465 }
466
467 #[test]
468 fn test_time_arithmetic_edge_cases() {
469 let time1 = Time::new(23, 59, 59);
470 let time2 = Time::new(0, 0, 1);
471 let result = time1 + time2;
472 assert_eq!(result, Time::new(24, 0, 0));
473 }
474
475 #[cfg(feature = "serde")]
476 mod serde_tests {
477 use super::*;
478 use serde_json;
479
480 #[test]
481 fn test_serde_microseconds() {
482 let time = Time::new(0, 0, 0);
483 let json = serde_json::to_string(&time).unwrap();
484 assert_eq!(json, "0");
485 let deserialized: Time = serde_json::from_str(&json).unwrap();
486 assert_eq!(deserialized, time);
487
488 let time = Time::new(12, 30, 45);
489 let expected_microseconds = time.to_microseconds();
490 let json = serde_json::to_string(&time).unwrap();
491 assert_eq!(json, expected_microseconds.to_string());
492 let deserialized: Time = serde_json::from_str(&json).unwrap();
493 assert_eq!(deserialized, time);
494
495 let time = Time::new(23, 59, 59);
496 let json = serde_json::to_string(&time).unwrap();
497 let deserialized: Time = serde_json::from_str(&json).unwrap();
498 assert_eq!(deserialized, time);
499 }
500
501 #[test]
502 fn test_serde_with_microseconds() {
503 let time = Time::new_with_microseconds(12, 30, 45, 123456);
504 let expected_microseconds = time.to_microseconds();
505 let json = serde_json::to_string(&time).unwrap();
506 assert_eq!(json, expected_microseconds.to_string());
507 let deserialized: Time = serde_json::from_str(&json).unwrap();
508 assert_eq!(deserialized, time);
509
510 let time = Time::new_with_microseconds(0, 0, 0, 999999);
511 let json = serde_json::to_string(&time).unwrap();
512 let deserialized: Time = serde_json::from_str(&json).unwrap();
513 assert_eq!(deserialized, time);
514 }
515
516 #[test]
517 fn test_serde_roundtrip() {
518 let times = vec![
519 Time::new(0, 0, 0),
520 Time::new(12, 0, 0),
521 Time::new(23, 59, 59),
522 Time::new_with_microseconds(12, 30, 45, 123456),
523 Time::new_with_microseconds(0, 0, 0, 999999),
524 ];
525
526 for time in times {
527 let json = serde_json::to_string(&time).unwrap();
528 let deserialized: Time = serde_json::from_str(&json).unwrap();
529 assert_eq!(deserialized, time, "Failed roundtrip for time: {}", time);
530 }
531 }
532
533 #[test]
534 fn test_serde_microseconds_fits_in_56_bits() {
535 let time = Time::new(23, 59, 59);
538 let microseconds = time.to_microseconds();
539 assert!(microseconds < (1u64 << 56), "Microseconds should fit in 56 bits");
540
541 let json = serde_json::to_string(&time).unwrap();
542 let deserialized: Time = serde_json::from_str(&json).unwrap();
543 assert_eq!(deserialized, time);
544 }
545 }
546}