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 let err: Error = TypeError::Temporal {
41 kind: TemporalKind::DateTimeOutOfRange,
42 message: "datetime out of representable range".into(),
43 fragment,
44 }
45 .into();
46 err
47 })
48}
49
50#[cfg(test)]
51pub mod tests {
52 use super::parse_datetime;
53 use crate::fragment::Fragment;
54
55 #[test]
56 fn test_basic() {
57 let fragment = Fragment::testing("2024-03-15T14:30:00");
58 let datetime = parse_datetime(fragment).unwrap();
59 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.000000000Z");
60 }
61
62 #[test]
63 fn test_with_timezone_z() {
64 let fragment = Fragment::testing("2024-03-15T14:30:00Z");
65 let datetime = parse_datetime(fragment).unwrap();
66 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.000000000Z");
67 }
68
69 #[test]
70 fn test_with_milliseconds() {
71 let fragment = Fragment::testing("2024-03-15T14:30:00.123Z");
72 let datetime = parse_datetime(fragment).unwrap();
73 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.123000000Z");
74 }
75
76 #[test]
77 fn test_with_microseconds() {
78 let fragment = Fragment::testing("2024-03-15T14:30:00.123456Z");
79 let datetime = parse_datetime(fragment).unwrap();
80 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.123456000Z");
81 }
82
83 #[test]
84 fn test_with_nanoseconds() {
85 let fragment = Fragment::testing("2024-03-15T14:30:00.123456789Z");
86 let datetime = parse_datetime(fragment).unwrap();
87 assert_eq!(datetime.to_string(), "2024-03-15T14:30:00.123456789Z");
88 }
89
90 #[test]
91 fn test_leap_year() {
92 let fragment = Fragment::testing("2024-02-29T00:00:00");
93 let datetime = parse_datetime(fragment).unwrap();
94 assert_eq!(datetime.to_string(), "2024-02-29T00:00:00.000000000Z");
95 }
96
97 #[test]
98 fn test_boundaries() {
99 let fragment = Fragment::testing("2000-01-01T00:00:00");
100 let datetime = parse_datetime(fragment).unwrap();
101 assert_eq!(datetime.to_string(), "2000-01-01T00:00:00.000000000Z");
102
103 let fragment = Fragment::testing("2024-12-31T23:59:59");
104 let datetime = parse_datetime(fragment).unwrap();
105 assert_eq!(datetime.to_string(), "2024-12-31T23:59:59.000000000Z");
106 }
107
108 #[test]
109 fn test_invalid_format() {
110 let fragment = Fragment::testing("2024-03-15");
111 let err = parse_datetime(fragment).unwrap_err();
112 assert_eq!(err.0.code, "TEMPORAL_002");
113 }
114
115 #[test]
116 fn test_invalid_date_format() {
117 let fragment = Fragment::testing("2024-03T14:30:00");
118 let err = parse_datetime(fragment).unwrap_err();
119 assert_eq!(err.0.code, "TEMPORAL_001");
120 }
121
122 #[test]
123 fn test_invalid_time_format() {
124 let fragment = Fragment::testing("2024-03-15T14:30");
125 let err = parse_datetime(fragment).unwrap_err();
126 assert_eq!(err.0.code, "TEMPORAL_003");
127 }
128
129 #[test]
130 fn test_invalid_year() {
131 let fragment = Fragment::testing("invalid-03-15T14:30:00");
132 let err = parse_datetime(fragment).unwrap_err();
133 assert_eq!(err.0.code, "TEMPORAL_005");
134 }
135
136 #[test]
137 fn test_invalid_date_values() {
138 let fragment = Fragment::testing("2024-13-32T23:30:40");
139 let err = parse_datetime(fragment).unwrap_err();
140 assert_eq!(err.0.code, "TEMPORAL_012");
141 }
142
143 #[test]
144 fn test_invalid_time_value() {
145 let fragment = Fragment::testing("2024-09-09T30:70:80");
146 let err = parse_datetime(fragment).unwrap_err();
147 assert_eq!(err.0.code, "TEMPORAL_013");
148 }
149
150 #[test]
151 fn test_invalid_fractional_seconds() {
152 let fragment = Fragment::testing("2024-03-15T14:30:00.123.456");
153 let err = parse_datetime(fragment).unwrap_err();
154 assert_eq!(err.0.code, "TEMPORAL_011");
155 }
156}