reifydb_value/value/temporal/parse/
date.rs1use crate::{
5 error::{Error, TemporalKind, TypeError},
6 fragment::Fragment,
7 value::Date,
8};
9
10pub fn parse_date(fragment: Fragment) -> Result<Date, Error> {
11 let value = fragment.text();
12 let parts: Vec<&str> = value.split('-').collect();
13
14 if parts.len() != 3 {
15 return Err(TypeError::Temporal {
16 kind: TemporalKind::InvalidDateFormat,
17 message: "invalid date format".into(),
18 fragment,
19 }
20 .into());
21 }
22
23 let year_str = parts[0].trim();
24 let month_str = parts[1].trim();
25 let day_str = parts[2].trim();
26
27 let mut offset = 0;
28 if year_str.is_empty() {
29 let year_frag = fragment.sub_fragment(offset, parts[0].len());
30 return Err(TypeError::Temporal {
31 kind: TemporalKind::EmptyDateComponent,
32 message: "empty date component".into(),
33 fragment: year_frag,
34 }
35 .into());
36 }
37 offset += parts[0].len() + 1;
38
39 if month_str.is_empty() {
40 let month_frag = fragment.sub_fragment(offset, parts[1].len());
41 return Err(TypeError::Temporal {
42 kind: TemporalKind::EmptyDateComponent,
43 message: "empty date component".into(),
44 fragment: month_frag,
45 }
46 .into());
47 }
48 offset += parts[1].len() + 1;
49
50 if day_str.is_empty() {
51 let day_frag = fragment.sub_fragment(offset, parts[2].len());
52 return Err(TypeError::Temporal {
53 kind: TemporalKind::EmptyDateComponent,
54 message: "empty date component".into(),
55 fragment: day_frag,
56 }
57 .into());
58 }
59
60 offset = 0;
61
62 if year_str.len() != 4 {
63 let year_frag = fragment.sub_fragment(offset, parts[0].len());
64 return Err(TypeError::Temporal {
65 kind: TemporalKind::InvalidYear,
66 message: format!("invalid year value '{}'", year_frag.text()),
67 fragment: year_frag,
68 }
69 .into());
70 }
71
72 let year = year_str.parse::<i32>().map_err(|_| {
73 let year_frag = fragment.sub_fragment(offset, parts[0].len());
74 Error::from(TypeError::Temporal {
75 kind: TemporalKind::InvalidYear,
76 message: format!("invalid year value '{}'", year_frag.text()),
77 fragment: year_frag,
78 })
79 })?;
80 offset += parts[0].len() + 1;
81
82 if month_str.len() != 2 {
83 let month_frag = fragment.sub_fragment(offset, parts[1].len());
84 return Err(TypeError::Temporal {
85 kind: TemporalKind::InvalidMonth,
86 message: format!("invalid month value '{}'", month_frag.text()),
87 fragment: month_frag,
88 }
89 .into());
90 }
91
92 let month = month_str.parse::<u32>().map_err(|_| {
93 let month_frag = fragment.sub_fragment(offset, parts[1].len());
94 Error::from(TypeError::Temporal {
95 kind: TemporalKind::InvalidMonth,
96 message: format!("invalid month value '{}'", month_frag.text()),
97 fragment: month_frag,
98 })
99 })?;
100 offset += parts[1].len() + 1;
101
102 if day_str.len() != 2 {
103 let day_frag = fragment.sub_fragment(offset, parts[2].len());
104 return Err(TypeError::Temporal {
105 kind: TemporalKind::InvalidDay,
106 message: format!("invalid day value '{}'", day_frag.text()),
107 fragment: day_frag,
108 }
109 .into());
110 }
111
112 let day = day_str.parse::<u32>().map_err(|_| {
113 let day_frag = fragment.sub_fragment(offset, parts[2].len());
114 Error::from(TypeError::Temporal {
115 kind: TemporalKind::InvalidDay,
116 message: format!("invalid day value '{}'", day_frag.text()),
117 fragment: day_frag,
118 })
119 })?;
120
121 Date::new(year, month, day).ok_or_else(|| {
122 Error::from(TypeError::Temporal {
123 kind: TemporalKind::InvalidDateValues,
124 message: "invalid date values".into(),
125 fragment,
126 })
127 })
128}
129
130#[cfg(test)]
131pub mod tests {
132 use super::parse_date;
133 use crate::fragment::Fragment;
134
135 #[test]
136 fn test_basic() {
137 let fragment = Fragment::testing("2024-03-15");
138 let date = parse_date(fragment).unwrap();
139 assert_eq!(date.to_string(), "2024-03-15");
140 }
141
142 #[test]
143 fn test_leap_year() {
144 let fragment = Fragment::testing("2024-02-29");
145 let date = parse_date(fragment).unwrap();
146 assert_eq!(date.to_string(), "2024-02-29");
147 }
148
149 #[test]
150 fn test_boundaries() {
151 let fragment = Fragment::testing("2000-01-01");
152 let date = parse_date(fragment).unwrap();
153 assert_eq!(date.to_string(), "2000-01-01");
154
155 let fragment = Fragment::testing("2024-12-31");
156 let date = parse_date(fragment).unwrap();
157 assert_eq!(date.to_string(), "2024-12-31");
158 }
159
160 #[test]
161 fn test_invalid_format() {
162 let fragment = Fragment::testing("2024-03");
163 let err = parse_date(fragment).unwrap_err();
164 assert_eq!(err.0.code, "TEMPORAL_001");
165 }
166
167 #[test]
168 fn test_invalid_year() {
169 let fragment = Fragment::testing("abcd-03-15");
170 let err = parse_date(fragment).unwrap_err();
171 assert_eq!(err.0.code, "TEMPORAL_005");
172 }
173
174 #[test]
175 fn test_invalid_month() {
176 let fragment = Fragment::testing("2024-invalid-15");
177 let err = parse_date(fragment).unwrap_err();
178 assert_eq!(err.0.code, "TEMPORAL_006");
179 }
180
181 #[test]
182 fn test_invalid_day() {
183 let fragment = Fragment::testing("2024-03-invalid");
184 let err = parse_date(fragment).unwrap_err();
185 assert_eq!(err.0.code, "TEMPORAL_007");
186 }
187
188 #[test]
189 fn test_invalid_date_values() {
190 let fragment = Fragment::testing("2024-13-32");
191 let err = parse_date(fragment).unwrap_err();
192 assert_eq!(err.0.code, "TEMPORAL_012");
193 }
194
195 #[test]
196 fn test_four_digit_year() {
197 let fragment = Fragment::testing("24-03-15");
200 let err = parse_date(fragment).unwrap_err();
201 assert_eq!(err.0.code, "TEMPORAL_005");
202
203 let fragment = Fragment::testing("024-03-15");
204 let err = parse_date(fragment).unwrap_err();
205 assert_eq!(err.0.code, "TEMPORAL_005");
206
207 let fragment = Fragment::testing("20240-03-15");
208 let err = parse_date(fragment).unwrap_err();
209 assert_eq!(err.0.code, "TEMPORAL_005");
210
211 let fragment = Fragment::testing("0024-03-15");
212 let date = parse_date(fragment).unwrap();
213 assert_eq!(date.to_string(), "0024-03-15");
214 }
215
216 #[test]
217 fn test_two_digit_month() {
218 let fragment = Fragment::testing("2024-3-15");
221 let err = parse_date(fragment).unwrap_err();
222 assert_eq!(err.0.code, "TEMPORAL_006");
223
224 let fragment = Fragment::testing("2024-003-15");
225 let err = parse_date(fragment).unwrap_err();
226 assert_eq!(err.0.code, "TEMPORAL_006");
227
228 let fragment = Fragment::testing("2024-03-15");
229 let date = parse_date(fragment).unwrap();
230 assert_eq!(date.to_string(), "2024-03-15");
231
232 let fragment = Fragment::testing("2024-0a-15");
233 let err = parse_date(fragment).unwrap_err();
234 assert_eq!(err.0.code, "TEMPORAL_006");
235
236 let fragment = Fragment::testing("2024- 3-15");
237 let err = parse_date(fragment).unwrap_err();
238 assert_eq!(err.0.code, "TEMPORAL_006");
239 }
240
241 #[test]
242 fn test_two_digit_day() {
243 let fragment = Fragment::testing("2024-03-5");
245 let err = parse_date(fragment).unwrap_err();
246 assert_eq!(err.0.code, "TEMPORAL_007");
247
248 let fragment = Fragment::testing("2024-03-015");
249 let err = parse_date(fragment).unwrap_err();
250 assert_eq!(err.0.code, "TEMPORAL_007");
251
252 let fragment = Fragment::testing("2024-03-05");
253 let date = parse_date(fragment).unwrap();
254 assert_eq!(date.to_string(), "2024-03-05");
255
256 let fragment = Fragment::testing("2024-03-1a");
257 let err = parse_date(fragment).unwrap_err();
258 assert_eq!(err.0.code, "TEMPORAL_007");
259
260 let fragment = Fragment::testing("2024-03- 5");
261 let err = parse_date(fragment).unwrap_err();
262 assert_eq!(err.0.code, "TEMPORAL_007");
263 }
264}