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
18impl Time {
19    pub fn new(hour: u64, minute: u64, second: u64) -> Self {
20        Self::new_with_microseconds(hour, minute, second, 0)
21    }
22
23    pub fn new_with_microseconds(hour: u64, minute: u64, second: u64, microseconds: u64) -> Self {
24        Time {
25            hour,
26            minute,
27            second,
28            microsecond: microseconds,
29        }
30    }
31
32    pub fn valid(&self) -> bool {
33        self.hour < 24 && self.minute < 60 && self.second < 60 && self.microsecond < 1000000
34    }
35
36    pub fn to_hh_mm_string(&self) -> String {
37        format!("{:02}:{:02}", self.hour, self.minute)
38    }
39
40    pub fn from_ms_dos_time(mut ms_dos_time: u16) -> Self {
41        let second = (ms_dos_time & 0x1f) << 1;
42        ms_dos_time >>= 5;
43        let minute = ms_dos_time & 0x3f;
44        ms_dos_time >>= 6;
45        Time::new(ms_dos_time as u64, minute as u64, second as u64)
46    }
47
48    pub fn from_seconds(mut seconds: u64) -> Self {
49        let hour = seconds / SECONDS_IN_HOUR;
50        seconds %= SECONDS_IN_HOUR;
51        let minute = seconds / SECONDS_IN_MINUTE;
52        seconds %= SECONDS_IN_MINUTE;
53        Time::new(hour, minute, seconds)
54    }
55
56    pub fn to_seconds(&self) -> u64 {
57        self.to_minutes() * SECONDS_IN_MINUTE + self.second
58    }
59
60    pub fn to_minutes(&self) -> u64 {
61        self.hour * MINUTES_IN_HOUR + self.minute
62    }
63
64    pub fn to_microseconds(&self) -> u64 {
65        self.to_seconds() * MICROSECONDS_IN_SECOND + self.microsecond
66    }
67
68    pub fn to_milliseconds(&self) -> u64 {
69        self.to_microseconds() / 1000
70    }
71
72    pub fn from_minutes(mut minutes: u64) -> Self {
73        let hour = minutes / MINUTES_IN_HOUR;
74        minutes %= MINUTES_IN_HOUR;
75        Time::new(hour, minutes, 0)
76    }
77
78    pub fn from_hours(hour: u64) -> Self {
79        Time::new(hour, 0, 0)
80    }
81
82    pub fn from_duration(duration: Duration) -> Time {
83        Self::from_seconds(duration.as_secs())
84    }
85
86    pub fn to_duration(&self) -> Duration {
87        Duration::from_secs(self.to_seconds())
88    }
89
90    pub fn normalize(&self) -> Time {
91        let mut second = self.microsecond / MICROSECONDS_IN_SECOND + self.second;
92        let microseconds = self.microsecond % MICROSECONDS_IN_SECOND;
93        let mut minute = second / SECONDS_IN_MINUTE + self.minute;
94        second = second % SECONDS_IN_MINUTE;
95        let hour = minute / MINUTES_IN_HOUR + self.hour;
96        minute = minute % MINUTES_IN_HOUR;
97        Self::new_with_microseconds(hour, minute, second, microseconds)
98    }
99}
100
101impl FromStr for Time {
102    type Err = DateError;
103
104    fn from_str(time_str: &str) -> Result<Self, Self::Err> {
105        let bytes = time_str.as_bytes();
106        let len = bytes.len();
107
108        if len < 8 || bytes[2] != b':' || bytes[5] != b':' {
109            return Err(DateErrorKind::WrongTimeStringFormat.into());
110        }
111        let hour = parse_digits(&bytes[0..2])?;
112
113        let minute = parse_digits(&bytes[3..5])?;
114        let (second, microseconds) = if len > 8 && bytes[8] == b'.' {
115            let second = parse_digits(&bytes[6..8])?;
116            let micro_str = &bytes[9..];
117            let micro_len = micro_str.len().min(6);
118            let mut microseconds = parse_digits(&micro_str[..micro_len])?;
119
120            for _ in micro_len..6 {
121                microseconds *= 10;
122            }
123
124            (second, microseconds)
125        } else {
126            let second = parse_digits(&bytes[6..8])?;
127            (second, 0)
128        };
129
130        Ok(Time::new_with_microseconds(
131            hour,
132            minute,
133            second,
134            microseconds,
135        ))
136    }
137}
138
139#[inline]
140fn parse_digits(bytes: &[u8]) -> Result<u64, DateError> {
141    let mut result = 0u64;
142    for &byte in bytes {
143        if byte < b'0' || byte > b'9' {
144            return Err(DateErrorKind::WrongTimeStringFormat.into());
145        }
146        result = result * 10 + (byte - b'0') as u64;
147    }
148    Ok(result)
149}
150
151impl fmt::Display for Time {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
154        if self.microsecond > 0 {
155            let mut divider = 1;
156            while self.microsecond % (divider * 10) == 0 {
157                divider *= 10;
158            }
159            let zeros = divider.ilog10();
160            let zeros = 6 - zeros as usize;
161            write!(f, ".{:0zeros$}", self.microsecond / divider)?;
162        }
163        Ok(())
164    }
165}
166
167impl fmt::Debug for Time {
168    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169        fmt::Display::fmt(self, f)
170    }
171}
172
173impl Ord for Time {
174    fn cmp(&self, other: &Self) -> Ordering {
175        if self.hour != other.hour {
176            return self.hour.cmp(&other.hour);
177        }
178        if self.minute != other.minute {
179            return self.minute.cmp(&other.minute);
180        }
181        if self.second != other.second {
182            return self.second.cmp(&other.second);
183        }
184        self.microsecond.cmp(&other.microsecond)
185    }
186}
187
188impl PartialOrd for Time {
189    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
190        Some(self.cmp(other))
191    }
192}
193
194impl std::ops::Sub for Time {
195    type Output = Time;
196
197    fn sub(self, rhs: Self) -> Self::Output {
198        let mut second = self.to_seconds() - rhs.to_seconds();
199        let mut microseconds = self.microsecond;
200        if self.microsecond < rhs.microsecond {
201            second -= 1;
202            microseconds += MICROSECONDS_IN_SECOND;
203        }
204        microseconds -= rhs.microsecond;
205        let mut result = Self::from_seconds(second);
206        result.microsecond = microseconds;
207        result
208    }
209}
210
211impl std::ops::Add for Time {
212    type Output = Time;
213
214    fn add(self, rhs: Self) -> Self::Output {
215        let mut total_microseconds = self.to_microseconds() + rhs.to_microseconds();
216
217        let microseconds = total_microseconds % MICROSECONDS_IN_SECOND;
218        total_microseconds /= MICROSECONDS_IN_SECOND;
219
220        let seconds = total_microseconds % SECONDS_IN_MINUTE;
221        total_microseconds /= SECONDS_IN_MINUTE;
222
223        let minutes = total_microseconds % MINUTES_IN_HOUR;
224        let hours = total_microseconds / MINUTES_IN_HOUR;
225
226        Time::new_with_microseconds(hours, minutes, seconds, microseconds)
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233
234    #[test]
235    fn test_ms_dos_time() {
236        assert_eq!(Time::from_ms_dos_time(0x7d1c), Time::new(15, 40, 56));
237    }
238
239    #[test]
240    fn test_time_cmp() {
241        assert!(Time::new(2, 12, 31) < Time::new(3, 1, 1));
242        assert!(Time::new(2, 2, 1) > Time::new(2, 1, 31));
243        assert!(Time::new(2, 3, 31) > Time::new(2, 3, 30));
244        assert_eq!(Time::new(2, 1, 1), Time::new(2, 1, 1));
245        assert!(
246            Time::new_with_microseconds(2, 3, 31, 11) > Time::new_with_microseconds(2, 3, 31, 10)
247        )
248    }
249
250    #[test]
251    fn test_from_str() -> Result<(), DateError> {
252        assert_eq!(
253            Time::from_str("21:10:05")?,
254            Time::new_with_microseconds(21, 10, 5, 0)
255        );
256        assert_eq!(
257            Time::from_str("21:10:05.779325")?,
258            Time::new_with_microseconds(21, 10, 5, 779325)
259        );
260        assert_eq!(
261            Time::from_str("21:10:05.77932599")?,
262            Time::new_with_microseconds(21, 10, 5, 779325)
263        );
264        assert_eq!(
265            Time::from_str("21:10:05.77932500")?,
266            Time::new_with_microseconds(21, 10, 5, 779325)
267        );
268        assert_eq!(
269            Time::from_str("21:10:05.779000")?,
270            Time::new_with_microseconds(21, 10, 5, 779000)
271        );
272        assert_eq!(
273            Time::from_str("21:10:05.779")?,
274            Time::new_with_microseconds(21, 10, 5, 779000)
275        );
276        assert_eq!(
277            Time::from_str("21:10:05.034104")?,
278            Time::new_with_microseconds(21, 10, 5, 034104)
279        );
280        Ok(())
281    }
282
283    #[test]
284    fn test_to_string() {
285        assert_eq!(
286            Time::new_with_microseconds(21, 10, 5, 0).to_string(),
287            "21:10:05"
288        );
289        assert_eq!(
290            Time::new_with_microseconds(21, 10, 5, 779325).to_string(),
291            "21:10:05.779325"
292        );
293        assert_eq!(
294            Time::new_with_microseconds(21, 10, 5, 779000).to_string(),
295            "21:10:05.779"
296        );
297        assert_eq!(
298            Time::new_with_microseconds(21, 10, 5, 779).to_string(),
299            "21:10:05.000779"
300        );
301        assert_eq!(
302            Time::new_with_microseconds(21, 10, 5, 34104).to_string(),
303            "21:10:05.034104"
304        );
305    }
306
307    #[test]
308    fn test_time_to_minutes() {
309        assert_eq!(Time::new_with_microseconds(2, 3, 31, 11).to_minutes(), 123);
310    }
311
312    #[test]
313    fn test_time_to_seconds() {
314        assert_eq!(Time::new_with_microseconds(2, 3, 31, 11).to_seconds(), 7411);
315    }
316
317    #[test]
318    fn test_time_to_microseconds() {
319        assert_eq!(
320            Time::new_with_microseconds(2, 3, 31, 11).to_microseconds(),
321            7411000011
322        );
323        assert_eq!(
324            Time::new_with_microseconds(2, 3, 31, 23560).to_microseconds(),
325            7411023560
326        );
327    }
328
329    #[test]
330    fn test_time_to_milliseconds() {
331        assert_eq!(
332            Time::new_with_microseconds(2, 3, 31, 23560).to_milliseconds(),
333            7411023
334        );
335    }
336
337    #[test]
338    fn test_time_normalize() {
339        assert_eq!(
340            Time::new_with_microseconds(42, 81, 74, 76543213).normalize(),
341            Time::new_with_microseconds(43, 23, 30, 543213)
342        );
343    }
344
345    #[test]
346    fn test_time_add() {
347        assert_eq!(
348            Time::new_with_microseconds(100, 59, 59, 999999)
349                + Time::new_with_microseconds(0, 0, 1, 1),
350            Time::new(101, 0, 1)
351        );
352    }
353
354    #[test]
355    fn test_time_sub() {
356        assert_eq!(
357            Time::new_with_microseconds(100, 0, 0, 0) - Time::new_with_microseconds(0, 0, 0, 1),
358            Time::new_with_microseconds(99, 59, 59, 999999)
359        );
360    }
361
362    #[test]
363    fn test_time_validation() {
364        assert!(Time::new(0, 0, 0).valid());
365        assert!(Time::new(23, 59, 59).valid());
366        assert!(Time::new(12, 30, 45).valid());
367        assert!(!Time::new(24, 0, 0).valid()); // Hour 24
368        assert!(!Time::new(25, 0, 0).valid()); // Hour 25
369        assert!(!Time::new(12, 60, 0).valid()); // Minute 60
370        assert!(!Time::new(12, 0, 60).valid()); // Second 60
371        assert!(!Time::new(12, 61, 0).valid()); // Minute 61
372        assert!(!Time::new(12, 0, 61).valid()); // Second 61
373    }
374
375    #[test]
376    fn test_time_from_str_invalid() {
377        assert!("invalid".parse::<Time>().is_err());
378        assert!("12:00".parse::<Time>().is_err());
379        assert!("12".parse::<Time>().is_err());
380        assert!("12-00-00".parse::<Time>().is_err());
381    }
382
383    #[test]
384    fn test_time_conversions() {
385        let time = Time::new(2, 30, 45);
386
387        assert_eq!(time.to_minutes(), 150); // 2*60 + 30
388        assert_eq!(time.to_seconds(), 9045); // 2*3600 + 30*60 + 45
389        assert_eq!(time.to_milliseconds(), 9045000);
390
391        assert_eq!(Time::from_minutes(150), Time::new(2, 30, 0));
392        assert_eq!(Time::from_hours(2), Time::new(2, 0, 0));
393        assert_eq!(Time::from_seconds(9045), Time::new(2, 30, 45));
394    }
395
396    #[test]
397    fn test_time_with_microseconds() {
398        let time = Time::new_with_microseconds(12, 30, 45, 123456);
399
400        assert_eq!(time.to_microseconds(), 45045123456);
401        assert_eq!(time.to_milliseconds(), 45045123);
402
403        assert_eq!(time.to_string(), "12:30:45.123456");
404        assert_eq!("12:30:45.123456".parse::<Time>().unwrap(), time);
405    }
406
407    #[test]
408    fn test_time_edge_cases() {
409        let midnight = Time::new(0, 0, 0);
410        assert_eq!(midnight.to_string(), "00:00:00");
411        assert_eq!(midnight.to_seconds(), 0);
412
413        let end_of_day = Time::new(23, 59, 59);
414        assert_eq!(end_of_day.to_string(), "23:59:59");
415        assert_eq!(end_of_day.to_seconds(), 86399);
416
417        let time_with_micros = Time::new_with_microseconds(0, 0, 0, 999999);
418        assert_eq!(time_with_micros.to_string(), "00:00:00.999999");
419    }
420
421    #[test]
422    fn test_time_duration_conversion() {
423        let time = Time::new(1, 30, 45);
424        let duration = time.to_duration();
425        assert_eq!(duration.as_secs(), 5445);
426
427        let from_duration = Time::from_duration(duration);
428        assert_eq!(from_duration, time);
429    }
430
431    #[test]
432    fn test_time_hh_mm_string() {
433        let time = Time::new(9, 5, 30);
434        assert_eq!(time.to_hh_mm_string(), "09:05");
435
436        let time2 = Time::new(23, 59, 0);
437        assert_eq!(time2.to_hh_mm_string(), "23:59");
438    }
439
440    #[test]
441    fn test_time_arithmetic_edge_cases() {
442        let time1 = Time::new(23, 59, 59);
443        let time2 = Time::new(0, 0, 1);
444        let result = time1 + time2;
445        assert_eq!(result, Time::new(24, 0, 0));
446    }
447}