reifydb_value/value/temporal/parse/
datetime.rs1use super::{date::parse_date, time::parse_time};
5use crate::{
6 error::{Error, TemporalKind, TypeError},
7 fragment::Fragment,
8 value::DateTime,
9};
10
11pub fn parse_datetime(fragment: Fragment) -> Result<DateTime, Error> {
12 let parts: Vec<&str> = fragment.text().split('T').collect();
13 if parts.len() != 2 {
14 return Err(TypeError::Temporal {
15 kind: TemporalKind::InvalidDateTimeFormat,
16 message: "invalid datetime format".into(),
17 fragment,
18 }
19 .into());
20 }
21
22 let date_offset = 0;
23 let date_fragment = fragment.sub_fragment(date_offset, parts[0].len());
24 let time_offset = parts[0].len() + 1;
25 let time_fragment = fragment.sub_fragment(time_offset, parts[1].len());
26
27 let date = parse_date(date_fragment)?;
28 let time = parse_time(time_fragment)?;
29
30 DateTime::new(
31 date.year(),
32 date.month(),
33 date.day(),
34 time.hour(),
35 time.minute(),
36 time.second(),
37 time.nanosecond(),
38 )
39 .ok_or_else(|| {
40 Error::from(TypeError::Temporal {
41 kind: TemporalKind::DateTimeOutOfRange,
42 message: "datetime out of representable range".into(),
43 fragment,
44 })
45 })
46}
47
48#[cfg(test)]
49pub mod tests {
50 use super::parse_datetime;
51 use crate::fragment::Fragment;
52
53 #[test]
54 fn test_basic() {
55 let fragment = Fragment::testing("2024-03-15T14:30:00");
56 let datetime = parse_datetime(fragment).unwrap();
57 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.000000000Z");
58 }
59
60 #[test]
61 fn test_with_timezone_z() {
62 let fragment = Fragment::testing("2024-03-15T14:30:00Z");
63 let datetime = parse_datetime(fragment).unwrap();
64 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.000000000Z");
65 }
66
67 #[test]
68 fn test_with_milliseconds() {
69 let fragment = Fragment::testing("2024-03-15T14:30:00.123Z");
70 let datetime = parse_datetime(fragment).unwrap();
71 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.123000000Z");
72 }
73
74 #[test]
75 fn test_with_microseconds() {
76 let fragment = Fragment::testing("2024-03-15T14:30:00.123456Z");
77 let datetime = parse_datetime(fragment).unwrap();
78 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.123456000Z");
79 }
80
81 #[test]
82 fn test_with_nanoseconds() {
83 let fragment = Fragment::testing("2024-03-15T14:30:00.123456789Z");
84 let datetime = parse_datetime(fragment).unwrap();
85 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.123456789Z");
86 }
87
88 #[test]
89 fn test_leap_year() {
90 let fragment = Fragment::testing("2024-02-29T00:00:00");
91 let datetime = parse_datetime(fragment).unwrap();
92 assert_eq!(datetime.to_string(), "2024-02-29T00:00:00.000000000Z");
93 }
94
95 #[test]
96 fn test_boundaries() {
97 let fragment = Fragment::testing("2000-01-01T00:00:00");
98 let datetime = parse_datetime(fragment).unwrap();
99 assert_eq!(datetime.to_string(), "2000-01-01T00:00:00.000000000Z");
100
101 let fragment = Fragment::testing("2024-12-31T23:59:59");
102 let datetime = parse_datetime(fragment).unwrap();
103 assert_eq!(datetime.to_string(), "2024-12-31T23:59:59.000000000Z");
104 }
105
106 #[test]
107 fn test_invalid_format() {
108 let fragment = Fragment::testing("2024-03-15");
109 let err = parse_datetime(fragment).unwrap_err();
110 assert_eq!(err.0.code, "TEMPORAL_002");
111 }
112
113 #[test]
114 fn test_invalid_date_format() {
115 let fragment = Fragment::testing("2024-03T14:30:00");
116 let err = parse_datetime(fragment).unwrap_err();
117 assert_eq!(err.0.code, "TEMPORAL_001");
118 }
119
120 #[test]
121 fn test_invalid_time_format() {
122 let fragment = Fragment::testing("2024-03-15T14:30");
123 let err = parse_datetime(fragment).unwrap_err();
124 assert_eq!(err.0.code, "TEMPORAL_003");
125 }
126
127 #[test]
128 fn test_invalid_year() {
129 let fragment = Fragment::testing("invalid-03-15T14:30:00");
130 let err = parse_datetime(fragment).unwrap_err();
131 assert_eq!(err.0.code, "TEMPORAL_005");
132 }
133
134 #[test]
135 fn test_invalid_date_values() {
136 let fragment = Fragment::testing("2024-13-32T23:30:40");
137 let err = parse_datetime(fragment).unwrap_err();
138 assert_eq!(err.0.code, "TEMPORAL_012");
139 }
140
141 #[test]
142 fn test_invalid_time_value() {
143 let fragment = Fragment::testing("2024-09-09T30:70:80");
144 let err = parse_datetime(fragment).unwrap_err();
145 assert_eq!(err.0.code, "TEMPORAL_013");
146 }
147
148 #[test]
149 fn test_invalid_fractional_seconds() {
150 let fragment = Fragment::testing("2024-03-15T14:30:00.123.456");
151 let err = parse_datetime(fragment).unwrap_err();
152 assert_eq!(err.0.code, "TEMPORAL_011");
153 }
154}