simple_datetime_rs/
time.rs

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// Serialize as u64 representing microseconds since midnight (fits in 56 bits)
19#[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        // Serialize as microseconds since midnight (max: 86,400,000,000, fits in 56 bits)
26        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(&micro_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()); // Hour 24
395        assert!(!Time::new(25, 0, 0).valid()); // Hour 25
396        assert!(!Time::new(12, 60, 0).valid()); // Minute 60
397        assert!(!Time::new(12, 0, 60).valid()); // Second 60
398        assert!(!Time::new(12, 61, 0).valid()); // Minute 61
399        assert!(!Time::new(12, 0, 61).valid()); // Second 61
400    }
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); // 2*60 + 30
415        assert_eq!(time.to_seconds(), 9045); // 2*3600 + 30*60 + 45
416        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            // Max microseconds in a day: 86,400,000,000
536            // u56 max: 2^56 - 1 = 72,057,594,037,927,935
537            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}