vibesql_types/temporal/
time.rs1use std::{cmp::Ordering, fmt, str::FromStr};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct Time {
11 pub hour: u8, pub minute: u8, pub second: u8, pub nanosecond: u32, }
16
17impl Time {
18 pub fn new(hour: u8, minute: u8, second: u8, nanosecond: u32) -> Result<Self, String> {
20 if hour > 23 {
21 return Err(format!("Invalid hour: {}", hour));
22 }
23 if minute > 59 {
24 return Err(format!("Invalid minute: {}", minute));
25 }
26 if second > 59 {
27 return Err(format!("Invalid second: {}", second));
28 }
29 if nanosecond > 999_999_999 {
30 return Err(format!("Invalid nanosecond: {}", nanosecond));
31 }
32 Ok(Time { hour, minute, second, nanosecond })
33 }
34}
35
36impl FromStr for Time {
37 type Err = String;
38
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 let (time_part, frac_part) = if let Some(dot_pos) = s.find('.') {
42 (&s[..dot_pos], Some(&s[dot_pos + 1..]))
43 } else {
44 (s, None)
45 };
46
47 let parts: Vec<&str> = time_part.split(':').collect();
48 if parts.len() != 3 {
49 return Err(format!("Invalid time format: '{}' (expected HH:MM:SS)", s));
50 }
51
52 let hour = parts[0].parse::<u8>().map_err(|_| format!("Invalid hour: '{}'", parts[0]))?;
53 let minute =
54 parts[1].parse::<u8>().map_err(|_| format!("Invalid minute: '{}'", parts[1]))?;
55 let second =
56 parts[2].parse::<u8>().map_err(|_| format!("Invalid second: '{}'", parts[2]))?;
57
58 let nanosecond = if let Some(frac) = frac_part {
60 let padded = format!("{:0<9}", frac);
62 let truncated = &padded[..9.min(padded.len())];
63 truncated
64 .parse::<u32>()
65 .map_err(|_| format!("Invalid fractional seconds: '{}'", frac))?
66 } else {
67 0
68 };
69
70 Time::new(hour, minute, second, nanosecond)
71 }
72}
73
74impl fmt::Display for Time {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 if self.nanosecond == 0 {
77 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)
78 } else {
79 let frac = format!("{:09}", self.nanosecond).trim_end_matches('0').to_string();
81 write!(f, "{:02}:{:02}:{:02}.{}", self.hour, self.minute, self.second, frac)
82 }
83 }
84}
85
86impl PartialOrd for Time {
87 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
88 Some(self.cmp(other))
89 }
90}
91
92impl Ord for Time {
93 fn cmp(&self, other: &Self) -> Ordering {
94 self.hour
95 .cmp(&other.hour)
96 .then_with(|| self.minute.cmp(&other.minute))
97 .then_with(|| self.second.cmp(&other.second))
98 .then_with(|| self.nanosecond.cmp(&other.nanosecond))
99 }
100}