simple_datetime_rs/
time.rs

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