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 let err: Error = TypeError::Temporal {
75 kind: TemporalKind::InvalidYear,
76 message: format!("invalid year value '{}'", year_frag.text()),
77 fragment: year_frag,
78 }
79 .into();
80 err
81 })?;
82 offset += parts[0].len() + 1;
83
84 if month_str.len() != 2 {
85 let month_frag = fragment.sub_fragment(offset, parts[1].len());
86 return Err(TypeError::Temporal {
87 kind: TemporalKind::InvalidMonth,
88 message: format!("invalid month value '{}'", month_frag.text()),
89 fragment: month_frag,
90 }
91 .into());
92 }
93
94 let month = month_str.parse::<u32>().map_err(|_| {
95 let month_frag = fragment.sub_fragment(offset, parts[1].len());
96 let err: Error = TypeError::Temporal {
97 kind: TemporalKind::InvalidMonth,
98 message: format!("invalid month value '{}'", month_frag.text()),
99 fragment: month_frag,
100 }
101 .into();
102 err
103 })?;
104 offset += parts[1].len() + 1;
105
106 if day_str.len() != 2 {
107 let day_frag = fragment.sub_fragment(offset, parts[2].len());
108 return Err(TypeError::Temporal {
109 kind: TemporalKind::InvalidDay,
110 message: format!("invalid day value '{}'", day_frag.text()),
111 fragment: day_frag,
112 }
113 .into());
114 }
115
116 let day = day_str.parse::<u32>().map_err(|_| {
117 let day_frag = fragment.sub_fragment(offset, parts[2].len());
118 let err: Error = TypeError::Temporal {
119 kind: TemporalKind::InvalidDay,
120 message: format!("invalid day value '{}'", day_frag.text()),
121 fragment: day_frag,
122 }
123 .into();
124 err
125 })?;
126
127 Date::new(year, month, day).ok_or_else(|| {
128 let err: Error = TypeError::Temporal {
129 kind: TemporalKind::InvalidDateValues,
130 message: "invalid date values".into(),
131 fragment,
132 }
133 .into();
134 err
135 })
136}
137
138#[cfg(test)]
139pub mod tests {
140 use super::parse_date;
141 use crate::fragment::Fragment;
142
143 #[test]
144 fn test_basic() {
145 let fragment = Fragment::testing("2024-03-15");
146 let date = parse_date(fragment).unwrap();
147 assert_eq!(date.to_string(), "2024-03-15");
148 }
149
150 #[test]
151 fn test_leap_year() {
152 let fragment = Fragment::testing("2024-02-29");
153 let date = parse_date(fragment).unwrap();
154 assert_eq!(date.to_string(), "2024-02-29");
155 }
156
157 #[test]
158 fn test_boundaries() {
159 let fragment = Fragment::testing("2000-01-01");
160 let date = parse_date(fragment).unwrap();
161 assert_eq!(date.to_string(), "2000-01-01");
162
163 let fragment = Fragment::testing("2024-12-31");
164 let date = parse_date(fragment).unwrap();
165 assert_eq!(date.to_string(), "2024-12-31");
166 }
167
168 #[test]
169 fn test_invalid_format() {
170 let fragment = Fragment::testing("2024-03");
171 let err = parse_date(fragment).unwrap_err();
172 assert_eq!(err.0.code, "TEMPORAL_001");
173 }
174
175 #[test]
176 fn test_invalid_year() {
177 let fragment = Fragment::testing("abcd-03-15");
178 let err = parse_date(fragment).unwrap_err();
179 assert_eq!(err.0.code, "TEMPORAL_005");
180 }
181
182 #[test]
183 fn test_invalid_month() {
184 let fragment = Fragment::testing("2024-invalid-15");
185 let err = parse_date(fragment).unwrap_err();
186 assert_eq!(err.0.code, "TEMPORAL_006");
187 }
188
189 #[test]
190 fn test_invalid_day() {
191 let fragment = Fragment::testing("2024-03-invalid");
192 let err = parse_date(fragment).unwrap_err();
193 assert_eq!(err.0.code, "TEMPORAL_007");
194 }
195
196 #[test]
197 fn test_invalid_date_values() {
198 let fragment = Fragment::testing("2024-13-32");
199 let err = parse_date(fragment).unwrap_err();
200 assert_eq!(err.0.code, "TEMPORAL_012");
201 }
202
203 #[test]
204 fn test_four_digit_year() {
205 let fragment = Fragment::testing("24-03-15");
207 let err = parse_date(fragment).unwrap_err();
208 assert_eq!(err.0.code, "TEMPORAL_005");
209
210 let fragment = Fragment::testing("024-03-15");
212 let err = parse_date(fragment).unwrap_err();
213 assert_eq!(err.0.code, "TEMPORAL_005");
214
215 let fragment = Fragment::testing("20240-03-15");
217 let err = parse_date(fragment).unwrap_err();
218 assert_eq!(err.0.code, "TEMPORAL_005");
219
220 let fragment = Fragment::testing("0024-03-15");
222 let date = parse_date(fragment).unwrap();
223 assert_eq!(date.to_string(), "0024-03-15");
224 }
225
226 #[test]
227 fn test_two_digit_month() {
228 let fragment = Fragment::testing("2024-3-15");
230 let err = parse_date(fragment).unwrap_err();
231 assert_eq!(err.0.code, "TEMPORAL_006");
232
233 let fragment = Fragment::testing("2024-003-15");
235 let err = parse_date(fragment).unwrap_err();
236 assert_eq!(err.0.code, "TEMPORAL_006");
237
238 let fragment = Fragment::testing("2024-03-15");
240 let date = parse_date(fragment).unwrap();
241 assert_eq!(date.to_string(), "2024-03-15");
242
243 let fragment = Fragment::testing("2024-0a-15");
245 let err = parse_date(fragment).unwrap_err();
246 assert_eq!(err.0.code, "TEMPORAL_006");
247
248 let fragment = Fragment::testing("2024- 3-15");
250 let err = parse_date(fragment).unwrap_err();
251 assert_eq!(err.0.code, "TEMPORAL_006");
252 }
253
254 #[test]
255 fn test_two_digit_day() {
256 let fragment = Fragment::testing("2024-03-5");
258 let err = parse_date(fragment).unwrap_err();
259 assert_eq!(err.0.code, "TEMPORAL_007");
260
261 let fragment = Fragment::testing("2024-03-015");
263 let err = parse_date(fragment).unwrap_err();
264 assert_eq!(err.0.code, "TEMPORAL_007");
265
266 let fragment = Fragment::testing("2024-03-05");
268 let date = parse_date(fragment).unwrap();
269 assert_eq!(date.to_string(), "2024-03-05");
270
271 let fragment = Fragment::testing("2024-03-1a");
273 let err = parse_date(fragment).unwrap_err();
274 assert_eq!(err.0.code, "TEMPORAL_007");
275
276 let fragment = Fragment::testing("2024-03- 5");
278 let err = parse_date(fragment).unwrap_err();
279 assert_eq!(err.0.code, "TEMPORAL_007");
280 }
281}