1use crate::value::interval::IntervalValue;
6use serde::{Deserialize, Serialize};
7use std::hash::Hash;
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash)]
11pub struct DateValue {
12 pub year: i32,
13 pub month: u32,
14 pub day: u32,
15}
16
17impl DateValue {
18 pub fn add_interval(&mut self, interval: &IntervalValue) {
20 if interval.months != 0 {
21 self.add_months(interval.months);
22 }
23
24 if interval.days != 0 {
25 self.add_days(interval.days as i64);
26 }
27 }
28
29 pub fn sub_interval(&mut self, interval: &IntervalValue) {
31 if interval.months != 0 {
32 self.add_months(-interval.months);
33 }
34
35 if interval.days != 0 {
36 self.add_days(-(interval.days as i64));
37 }
38 }
39
40 fn add_months(&mut self, months: i32) {
41 let mut new_month = self.month as i32 + months;
42 let mut year_delta = 0;
43
44 while new_month > 12 {
45 new_month -= 12;
46 year_delta += 1;
47 }
48
49 while new_month < 1 {
50 new_month += 12;
51 year_delta -= 1;
52 }
53
54 self.year += year_delta;
55 self.month = new_month as u32;
56
57 self.normalize_day();
58 }
59
60 fn add_days(&mut self, days: i64) {
61 let total_days = Self::to_days(self) + days;
62 *self = Self::from_days(total_days);
63 }
64
65 fn normalize_day(&mut self) {
66 let days_in_month = Self::days_in_month(self.year, self.month);
67 if self.day > days_in_month {
68 self.day = days_in_month;
69 }
70 }
71
72 fn days_in_month(year: i32, month: u32) -> u32 {
73 match month {
74 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
75 4 | 6 | 9 | 11 => 30,
76 2 => {
77 if Self::is_leap_year(year) {
78 29
79 } else {
80 28
81 }
82 }
83 _ => panic!("Invalid month"),
84 }
85 }
86
87 fn is_leap_year(year: i32) -> bool {
88 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
89 }
90
91 pub fn to_days(&self) -> i64 {
92 let year = self.year as i64;
93 let month = self.month as i64;
94 let day = self.day as i64;
95
96 let a = (14 - month) / 12;
97 let y = year + 4800 - a;
98 let m = month + 12 * a - 3;
99
100 day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045
101 }
102
103 pub fn from_days(days: i64) -> Self {
106 let a = days + 32044;
107 let b = (4 * a + 3) / 146097;
108 let c = a - (146097 * b) / 4;
109 let d = (4 * c + 3) / 1461;
110 let e = c - (1461 * d) / 4;
111 let m = (5 * e + 2) / 153;
112
113 let day = e - (153 * m + 2) / 5 + 1;
114 let month = m + 3 - 12 * (m / 10);
115 let year = 100 * b + d - 4800 + m / 10;
116
117 DateValue {
118 year: year as i32,
119 month: month as u32,
120 day: day as u32,
121 }
122 }
123}
124
125impl Default for DateValue {
126 fn default() -> Self {
127 DateValue {
128 year: 1970,
129 month: 1,
130 day: 1,
131 }
132 }
133}
134
135impl DateValue {
136 pub fn estimated_size(&self) -> usize {
138 std::mem::size_of::<Self>()
139 }
140}
141
142impl std::fmt::Display for DateValue {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
145 }
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Default)]
150pub struct TimeValue {
151 pub hour: u32,
152 pub minute: u32,
153 pub sec: u32,
154 pub microsec: u32,
155}
156
157impl TimeValue {
158 pub fn add_interval(&mut self, interval: &IntervalValue) {
160 let total_microseconds = interval.microseconds;
161 let mut new_microseconds = self.microsec as i64 + total_microseconds;
162
163 while new_microseconds >= 86_400_000_000 {
164 new_microseconds -= 86_400_000_000;
165 }
166 while new_microseconds < 0 {
167 new_microseconds += 86_400_000_000;
168 }
169
170 self.microsec = (new_microseconds % 1_000_000) as u32;
171 let total_seconds = new_microseconds / 1_000_000;
172
173 let mut total_time =
174 self.hour as i64 * 3600 + self.minute as i64 * 60 + self.sec as i64 + total_seconds;
175
176 while total_time >= 86_400 {
177 total_time -= 86_400;
178 }
179 while total_time < 0 {
180 total_time += 86_400;
181 }
182
183 self.hour = (total_time / 3600) as u32;
184 self.minute = ((total_time % 3600) / 60) as u32;
185 self.sec = (total_time % 60) as u32;
186 }
187
188 pub fn sub_interval(&mut self, interval: &IntervalValue) {
190 let neg_interval = interval.neg();
191 self.add_interval(&neg_interval);
192 }
193}
194
195impl TimeValue {
196 pub fn estimated_size(&self) -> usize {
198 std::mem::size_of::<Self>()
199 }
200}
201
202impl std::fmt::Display for TimeValue {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.sec)
205 }
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash)]
210pub struct DateTimeValue {
211 pub year: i32,
212 pub month: u32,
213 pub day: u32,
214 pub hour: u32,
215 pub minute: u32,
216 pub sec: u32,
217 pub microsec: u32,
218}
219
220impl DateTimeValue {
221 pub fn add_interval(&mut self, interval: &IntervalValue) {
223 let mut date = DateValue {
224 year: self.year,
225 month: self.month,
226 day: self.day,
227 };
228 date.add_interval(interval);
229
230 let mut time = TimeValue {
231 hour: self.hour,
232 minute: self.minute,
233 sec: self.sec,
234 microsec: self.microsec,
235 };
236 time.add_interval(interval);
237
238 self.year = date.year;
239 self.month = date.month;
240 self.day = date.day;
241 self.hour = time.hour;
242 self.minute = time.minute;
243 self.sec = time.sec;
244 self.microsec = time.microsec;
245 }
246
247 pub fn sub_interval(&mut self, interval: &IntervalValue) {
249 let mut date = DateValue {
250 year: self.year,
251 month: self.month,
252 day: self.day,
253 };
254 date.sub_interval(interval);
255
256 let mut time = TimeValue {
257 hour: self.hour,
258 minute: self.minute,
259 sec: self.sec,
260 microsec: self.microsec,
261 };
262 time.sub_interval(interval);
263
264 self.year = date.year;
265 self.month = date.month;
266 self.day = date.day;
267 self.hour = time.hour;
268 self.minute = time.minute;
269 self.sec = time.sec;
270 self.microsec = time.microsec;
271 }
272}
273
274impl Default for DateTimeValue {
275 fn default() -> Self {
276 DateTimeValue {
277 year: 1970,
278 month: 1,
279 day: 1,
280 hour: 0,
281 minute: 0,
282 sec: 0,
283 microsec: 0,
284 }
285 }
286}
287
288impl DateTimeValue {
289 pub fn estimated_size(&self) -> usize {
291 std::mem::size_of::<Self>()
292 }
293
294 pub fn to_micros(&self) -> i64 {
302 let date = DateValue {
303 year: self.year,
304 month: self.month,
305 day: self.day,
306 };
307 (date.to_days() - 2_440_588) * 86_400_000_000
311 + i64::from(self.hour) * 3_600_000_000
312 + i64::from(self.minute) * 60_000_000
313 + i64::from(self.sec) * 1_000_000
314 + i64::from(self.microsec)
315 }
316
317 pub fn from_micros(micros: i64) -> Self {
322 let days = micros.div_euclid(86_400_000_000) + 2_440_588;
323 let time_of_day = micros.rem_euclid(86_400_000_000);
324 let date = DateValue::from_days(days);
325 DateTimeValue {
326 year: date.year,
327 month: date.month,
328 day: date.day,
329 hour: (time_of_day / 3_600_000_000) as u32,
330 minute: ((time_of_day % 3_600_000_000) / 60_000_000) as u32,
331 sec: ((time_of_day % 60_000_000) / 1_000_000) as u32,
332 microsec: (time_of_day % 1_000_000) as u32,
333 }
334 }
335}
336
337impl std::fmt::Display for DateTimeValue {
338 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
339 write!(
340 f,
341 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
342 self.year, self.month, self.day, self.hour, self.minute, self.sec
343 )
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_to_micros_epoch_is_zero() {
353 assert_eq!(DateTimeValue::default().to_micros(), 0);
354 }
355
356 #[test]
357 fn test_to_micros_before_epoch_is_negative() {
358 let dt = DateTimeValue {
359 year: 1969,
360 month: 12,
361 day: 31,
362 hour: 23,
363 minute: 59,
364 sec: 59,
365 microsec: 999_999,
366 };
367 assert_eq!(dt.to_micros(), -1);
368 }
369
370 #[test]
371 fn test_micros_roundtrip() {
372 let dt = DateTimeValue {
373 year: 2024,
374 month: 2,
375 day: 29,
376 hour: 12,
377 minute: 34,
378 sec: 56,
379 microsec: 789_012,
380 };
381 assert_eq!(DateTimeValue::from_micros(dt.to_micros()), dt);
382 let pre_epoch = DateTimeValue {
383 year: 1900,
384 month: 6,
385 day: 15,
386 hour: 8,
387 minute: 30,
388 sec: 0,
389 microsec: 1,
390 };
391 assert_eq!(DateTimeValue::from_micros(pre_epoch.to_micros()), pre_epoch);
392 }
393
394 #[test]
395 fn test_micros_ordering_matches_field_order() {
396 let a = DateTimeValue {
397 year: 2023,
398 month: 12,
399 day: 31,
400 hour: 23,
401 minute: 59,
402 sec: 59,
403 microsec: 999_999,
404 };
405 let b = DateTimeValue {
406 year: 2024,
407 month: 1,
408 day: 1,
409 hour: 0,
410 minute: 0,
411 sec: 0,
412 microsec: 0,
413 };
414 assert!(a.to_micros() < b.to_micros());
415 }
416}