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(µ_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()); 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()); }
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); assert_eq!(time.to_seconds(), 9045); 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}