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