xsd_types/lexical/
time.rs1use static_regular_grammar::RegularGrammar;
2
3use crate::{utils::byte_index_of, InvalidTimeValue};
4
5use super::{date_time::parse_seconds_decimal, parse_timezone, Lexical, LexicalFormOf};
6
7#[derive(RegularGrammar, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[grammar(sized(TimeBuf, derive(PartialEq, Eq, PartialOrd, Ord, Hash)))]
29pub struct Time(str);
30
31impl Time {
32 fn parts(&self) -> Parts {
33 let seconds_end =
34 byte_index_of(self.0.as_bytes(), 8, [b'+', b'-', b'Z']).unwrap_or(self.0.len());
35 Parts {
36 hours: &self.0[..2],
37 minutes: &self.0[3..5],
38 seconds: &self.0[6..seconds_end],
39 timezone: if seconds_end == self.0.len() {
40 None
41 } else {
42 Some(&self.0[seconds_end..])
43 },
44 }
45 }
46}
47
48impl Lexical for Time {
49 type Error = InvalidTime<String>;
50
51 fn parse(value: &str) -> Result<&Self, Self::Error> {
52 Self::new(value).map_err(|_| InvalidTime(value.to_owned()))
53 }
54}
55
56impl LexicalFormOf<crate::Time> for Time {
57 type ValueError = InvalidTimeValue;
58
59 fn try_as_value(&self) -> Result<crate::Time, Self::ValueError> {
60 self.parts().to_time()
61 }
62}
63
64#[derive(Debug, PartialEq, Eq)]
65pub struct Parts<'a> {
66 pub hours: &'a str,
67 pub minutes: &'a str,
68 pub seconds: &'a str,
69 pub timezone: Option<&'a str>,
70}
71
72impl<'a> Parts<'a> {
73 pub fn new(
74 hours: &'a str,
75 minutes: &'a str,
76 seconds: &'a str,
77 timezone: Option<&'a str>,
78 ) -> Self {
79 Self {
80 hours,
81 minutes,
82 seconds,
83 timezone,
84 }
85 }
86
87 fn to_time(&self) -> Result<crate::Time, crate::InvalidTimeValue> {
88 let (seconds, nanoseconds) = parse_seconds_decimal(self.seconds);
89
90 let time = chrono::NaiveTime::from_hms_nano_opt(
91 self.hours.parse().unwrap(),
92 self.minutes.parse().unwrap(),
93 seconds,
94 nanoseconds,
95 )
96 .ok_or(crate::InvalidTimeValue)?;
97
98 Ok(crate::Time::new(time, self.timezone.map(parse_timezone)))
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn parsing() {
108 let vectors = [
109 (
110 "13:07:12+01:00",
111 Parts::new("13", "07", "12", Some("+01:00")),
112 ),
113 (
114 "12:00:00-05:00",
115 Parts::new("12", "00", "00", Some("-05:00")),
116 ),
117 (
118 "12:00:00.00001-05:00",
119 Parts::new("12", "00", "00.00001", Some("-05:00")),
120 ),
121 ];
122
123 for (input, parts) in vectors {
124 let lexical_repr = Time::new(input).unwrap();
125 assert_eq!(lexical_repr.parts(), parts);
126
127 let value = lexical_repr.try_as_value().unwrap();
128 assert_eq!(value.to_string().as_str(), input)
129 }
130 }
131}