1use std::fmt::{Display, Formatter};
5
6use serde::{
7 Deserialize, Deserializer, Serialize, Serializer,
8 de::{self, Visitor},
9};
10
11use crate::value::{date::Date, time::Time};
12
13#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub struct DateTime {
19 seconds: i64,
21 nanos: u32,
23}
24
25impl Default for DateTime {
26 fn default() -> Self {
27 Self {
28 seconds: 0,
29 nanos: 0,
30 } }
32}
33
34impl DateTime {
35 pub fn new(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, nano: u32) -> Option<Self> {
36 let date = Date::new(year, month, day)?;
38
39 let time = Time::new(hour, min, sec, nano)?;
41
42 let days = date.to_days_since_epoch() as i64;
44 let date_seconds = days * 86400;
45
46 let time_nanos = time.to_nanos_since_midnight();
48 let time_seconds = (time_nanos / 1_000_000_000) as i64;
49 let time_nano_part = (time_nanos % 1_000_000_000) as u32;
50
51 Some(Self {
52 seconds: date_seconds + time_seconds,
53 nanos: time_nano_part,
54 })
55 }
56
57 pub fn from_ymd_hms(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> Result<Self, String> {
58 Self::new(year, month, day, hour, min, sec, 0).ok_or_else(|| {
59 format!("Invalid datetime: {}-{:02}-{:02} {:02}:{:02}:{:02}", year, month, day, hour, min, sec)
60 })
61 }
62
63 pub fn from_timestamp(timestamp: i64) -> Result<Self, String> {
64 Ok(Self {
65 seconds: timestamp,
66 nanos: 0,
67 })
68 }
69
70 pub fn from_timestamp_millis(millis: u64) -> Self {
71 let seconds = (millis / 1000) as i64;
72 let nanos = ((millis % 1000) * 1_000_000) as u32;
73 Self {
74 seconds,
75 nanos,
76 }
77 }
78
79 pub fn from_timestamp_nanos(nanos: u128) -> Self {
80 let seconds = (nanos / 1_000_000_000) as i64;
81 let nanos = (nanos % 1_000_000_000) as u32;
82
83 Self {
84 seconds,
85 nanos,
86 }
87 }
88
89 pub fn timestamp(&self) -> i64 {
90 self.seconds
91 }
92
93 pub fn timestamp_millis(&self) -> i64 {
94 self.seconds * 1000 + (self.nanos / 1_000_000) as i64
95 }
96
97 pub fn timestamp_nanos(&self) -> i64 {
98 self.seconds.saturating_mul(1_000_000_000).saturating_add(self.nanos as i64)
99 }
100
101 pub fn date(&self) -> Date {
102 let days = (self.seconds / 86400) as i32;
104 Date::from_days_since_epoch(days).unwrap()
105 }
106
107 pub fn time(&self) -> Time {
108 let seconds_in_day = self.seconds % 86400;
110 let seconds_in_day = if seconds_in_day < 0 {
111 seconds_in_day + 86400
112 } else {
113 seconds_in_day
114 } as u64;
115
116 let nanos_in_day = seconds_in_day * 1_000_000_000 + self.nanos as u64;
117 Time::from_nanos_since_midnight(nanos_in_day).unwrap()
118 }
119
120 pub fn to_nanos_since_epoch(&self) -> i64 {
122 self.timestamp_nanos()
123 }
124
125 pub fn from_nanos_since_epoch(nanos: i64) -> Self {
127 let seconds = nanos / 1_000_000_000;
128 let nano_part = nanos % 1_000_000_000;
129
130 let (seconds, nanos) = if nanos < 0 && nano_part != 0 {
132 (seconds - 1, (1_000_000_000 - nano_part.abs()) as u32)
133 } else {
134 (seconds, nano_part.abs() as u32)
135 };
136
137 Self {
138 seconds,
139 nanos,
140 }
141 }
142
143 pub fn from_parts(seconds: i64, nanos: u32) -> Result<Self, String> {
145 if nanos >= 1_000_000_000 {
146 return Err(format!("Invalid nanoseconds: {} (must be < 1_000_000_000)", nanos));
147 }
148 Ok(Self {
149 seconds,
150 nanos,
151 })
152 }
153
154 pub fn to_parts(&self) -> (i64, u32) {
156 (self.seconds, self.nanos)
157 }
158
159 pub fn year(&self) -> i32 {
161 self.date().year()
162 }
163
164 pub fn month(&self) -> u32 {
166 self.date().month()
167 }
168
169 pub fn day(&self) -> u32 {
171 self.date().day()
172 }
173
174 pub fn hour(&self) -> u32 {
176 self.time().hour()
177 }
178
179 pub fn minute(&self) -> u32 {
181 self.time().minute()
182 }
183
184 pub fn second(&self) -> u32 {
186 self.time().second()
187 }
188
189 pub fn nanosecond(&self) -> u32 {
191 self.time().nanosecond()
192 }
193}
194
195impl Display for DateTime {
196 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
197 let date = self.date();
198 let time = self.time();
199
200 write!(f, "{}T{}Z", date, time)
202 }
203}
204
205impl Serialize for DateTime {
207 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208 where
209 S: Serializer,
210 {
211 serializer.serialize_str(&self.to_string())
212 }
213}
214
215struct DateTimeVisitor;
216
217impl<'de> Visitor<'de> for DateTimeVisitor {
218 type Value = DateTime;
219
220 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
221 formatter.write_str("a datetime in ISO 8601 format (YYYY-MM-DDTHH:MM:SS[.nnnnnnnnn]Z)")
222 }
223
224 fn visit_str<E>(self, value: &str) -> Result<DateTime, E>
225 where
226 E: de::Error,
227 {
228 let value = value.strip_suffix('Z').unwrap_or(value);
232
233 let parts: Vec<&str> = value.split('T').collect();
235 if parts.len() != 2 {
236 return Err(E::custom(format!("invalid datetime format: {}", value)));
237 }
238
239 let date_parts: Vec<&str> = parts[0].split('-').collect();
241 if date_parts.len() != 3 {
242 return Err(E::custom(format!("invalid date format: {}", parts[0])));
243 }
244
245 let (year_str, month_str, day_str) = if date_parts[0].is_empty() && date_parts.len() == 4 {
247 (format!("-{}", date_parts[1]), date_parts[2], date_parts[3])
249 } else {
250 (date_parts[0].to_string(), date_parts[1], date_parts[2])
251 };
252
253 let year = year_str.parse::<i32>().map_err(|_| E::custom(format!("invalid year: {}", year_str)))?;
254 let month = month_str.parse::<u32>().map_err(|_| E::custom(format!("invalid month: {}", month_str)))?;
255 let day = day_str.parse::<u32>().map_err(|_| E::custom(format!("invalid day: {}", day_str)))?;
256
257 let (time_part, nano_part) = if let Some(dot_pos) = parts[1].find('.') {
259 (&parts[1][..dot_pos], Some(&parts[1][dot_pos + 1..]))
260 } else {
261 (parts[1], None)
262 };
263
264 let time_parts: Vec<&str> = time_part.split(':').collect();
265 if time_parts.len() != 3 {
266 return Err(E::custom(format!("invalid time format: {}", parts[1])));
267 }
268
269 let hour = time_parts[0]
270 .parse::<u32>()
271 .map_err(|_| E::custom(format!("invalid hour: {}", time_parts[0])))?;
272 let minute = time_parts[1]
273 .parse::<u32>()
274 .map_err(|_| E::custom(format!("invalid minute: {}", time_parts[1])))?;
275 let second = time_parts[2]
276 .parse::<u32>()
277 .map_err(|_| E::custom(format!("invalid second: {}", time_parts[2])))?;
278
279 let nano = if let Some(nano_str) = nano_part {
280 let padded = if nano_str.len() < 9 {
282 format!("{:0<9}", nano_str)
283 } else {
284 nano_str[..9].to_string()
285 };
286 padded.parse::<u32>().map_err(|_| E::custom(format!("invalid nanoseconds: {}", nano_str)))?
287 } else {
288 0
289 };
290
291 DateTime::new(year, month, day, hour, minute, second, nano).ok_or_else(|| {
292 E::custom(format!(
293 "invalid datetime: {}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}Z",
294 year, month, day, hour, minute, second, nano
295 ))
296 })
297 }
298}
299
300impl<'de> Deserialize<'de> for DateTime {
301 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
302 where
303 D: Deserializer<'de>,
304 {
305 deserializer.deserialize_str(DateTimeVisitor)
306 }
307}
308
309#[cfg(test)]
310pub mod tests {
311 use super::*;
312
313 #[test]
314 fn test_datetime_display_standard_format() {
315 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
316 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123456789Z");
317
318 let datetime = DateTime::new(2000, 1, 1, 0, 0, 0, 0).unwrap();
319 assert_eq!(format!("{}", datetime), "2000-01-01T00:00:00.000000000Z");
320
321 let datetime = DateTime::new(1999, 12, 31, 23, 59, 59, 999999999).unwrap();
322 assert_eq!(format!("{}", datetime), "1999-12-31T23:59:59.999999999Z");
323 }
324
325 #[test]
326 fn test_datetime_display_millisecond_precision() {
327 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123000000).unwrap();
329 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123000000Z");
330
331 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 001000000).unwrap();
332 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.001000000Z");
333
334 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 999000000).unwrap();
335 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.999000000Z");
336 }
337
338 #[test]
339 fn test_datetime_display_microsecond_precision() {
340 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456000).unwrap();
342 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123456000Z");
343
344 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 000001000).unwrap();
345 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.000001000Z");
346
347 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 999999000).unwrap();
348 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.999999000Z");
349 }
350
351 #[test]
352 fn test_datetime_display_nanosecond_precision() {
353 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
355 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123456789Z");
356
357 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 000000001).unwrap();
358 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.000000001Z");
359
360 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 999999999).unwrap();
361 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.999999999Z");
362 }
363
364 #[test]
365 fn test_datetime_display_zero_fractional_seconds() {
366 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 0).unwrap();
367 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.000000000Z");
368
369 let datetime = DateTime::new(2024, 3, 15, 0, 0, 0, 0).unwrap();
370 assert_eq!(format!("{}", datetime), "2024-03-15T00:00:00.000000000Z");
371 }
372
373 #[test]
374 fn test_datetime_display_edge_times() {
375 let datetime = DateTime::new(2024, 3, 15, 0, 0, 0, 0).unwrap();
377 assert_eq!(format!("{}", datetime), "2024-03-15T00:00:00.000000000Z");
378
379 let datetime = DateTime::new(2024, 3, 15, 23, 59, 59, 999999999).unwrap();
381 assert_eq!(format!("{}", datetime), "2024-03-15T23:59:59.999999999Z");
382
383 let datetime = DateTime::new(2024, 3, 15, 12, 0, 0, 0).unwrap();
385 assert_eq!(format!("{}", datetime), "2024-03-15T12:00:00.000000000Z");
386 }
387
388 #[test]
389 fn test_datetime_display_unix_epoch() {
390 let datetime = DateTime::new(1970, 1, 1, 0, 0, 0, 0).unwrap();
391 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
392
393 let datetime = DateTime::new(1970, 1, 1, 0, 0, 1, 0).unwrap();
394 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:01.000000000Z");
395 }
396
397 #[test]
398 fn test_datetime_display_leap_year() {
399 let datetime = DateTime::new(2024, 2, 29, 12, 30, 45, 123456789).unwrap();
400 assert_eq!(format!("{}", datetime), "2024-02-29T12:30:45.123456789Z");
401
402 let datetime = DateTime::new(2000, 2, 29, 0, 0, 0, 0).unwrap();
403 assert_eq!(format!("{}", datetime), "2000-02-29T00:00:00.000000000Z");
404 }
405
406 #[test]
407 fn test_datetime_display_boundary_dates() {
408 let datetime = DateTime::new(1, 1, 1, 0, 0, 0, 0).unwrap();
410 assert_eq!(format!("{}", datetime), "0001-01-01T00:00:00.000000000Z");
411
412 let datetime = DateTime::new(9999, 12, 31, 23, 59, 59, 999999999).unwrap();
414 assert_eq!(format!("{}", datetime), "9999-12-31T23:59:59.999999999Z");
415
416 let datetime = DateTime::new(1900, 1, 1, 0, 0, 0, 0).unwrap();
418 assert_eq!(format!("{}", datetime), "1900-01-01T00:00:00.000000000Z");
419
420 let datetime = DateTime::new(2000, 1, 1, 0, 0, 0, 0).unwrap();
421 assert_eq!(format!("{}", datetime), "2000-01-01T00:00:00.000000000Z");
422
423 let datetime = DateTime::new(2100, 1, 1, 0, 0, 0, 0).unwrap();
424 assert_eq!(format!("{}", datetime), "2100-01-01T00:00:00.000000000Z");
425 }
426
427 #[test]
428 fn test_datetime_display_default() {
429 let datetime = DateTime::default();
430 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
431 }
432
433 #[test]
434 fn test_datetime_display_all_hours() {
435 for hour in 0..24 {
436 let datetime = DateTime::new(2024, 3, 15, hour, 30, 45, 123456789).unwrap();
437 let expected = format!("2024-03-15T{:02}:30:45.123456789Z", hour);
438 assert_eq!(format!("{}", datetime), expected);
439 }
440 }
441
442 #[test]
443 fn test_datetime_display_all_minutes() {
444 for minute in 0..60 {
445 let datetime = DateTime::new(2024, 3, 15, 14, minute, 45, 123456789).unwrap();
446 let expected = format!("2024-03-15T14:{:02}:45.123456789Z", minute);
447 assert_eq!(format!("{}", datetime), expected);
448 }
449 }
450
451 #[test]
452 fn test_datetime_display_all_seconds() {
453 for second in 0..60 {
454 let datetime = DateTime::new(2024, 3, 15, 14, 30, second, 123456789).unwrap();
455 let expected = format!("2024-03-15T14:30:{:02}.123456789Z", second);
456 assert_eq!(format!("{}", datetime), expected);
457 }
458 }
459
460 #[test]
461 fn test_datetime_display_from_timestamp() {
462 let datetime = DateTime::from_timestamp(0).unwrap();
463 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
464
465 let datetime = DateTime::from_timestamp(1234567890).unwrap();
466 assert_eq!(format!("{}", datetime), "2009-02-13T23:31:30.000000000Z");
467 }
468
469 #[test]
470 fn test_datetime_display_from_timestamp_millis() {
471 let datetime = DateTime::from_timestamp_millis(1234567890123);
472 assert_eq!(format!("{}", datetime), "2009-02-13T23:31:30.123000000Z");
473
474 let datetime = DateTime::from_timestamp_millis(0);
475 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
476 }
477
478 #[test]
479 fn test_datetime_display_from_parts() {
480 let datetime = DateTime::from_parts(1234567890, 123456789).unwrap();
481 assert_eq!(format!("{}", datetime), "2009-02-13T23:31:30.123456789Z");
482
483 let datetime = DateTime::from_parts(0, 0).unwrap();
484 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
485 }
486
487 #[test]
488 fn test_datetime_roundtrip() {
489 let test_cases = [
490 (1970, 1, 1, 0, 0, 0, 0),
491 (2024, 3, 15, 14, 30, 45, 123456789),
492 (2000, 2, 29, 23, 59, 59, 999999999),
493 ];
494
495 for (y, m, d, h, min, s, n) in test_cases {
496 let datetime = DateTime::new(y, m, d, h, min, s, n).unwrap();
497 let nanos = datetime.to_nanos_since_epoch();
498 let recovered = DateTime::from_nanos_since_epoch(nanos);
499
500 assert_eq!(datetime.year(), recovered.year());
501 assert_eq!(datetime.month(), recovered.month());
502 assert_eq!(datetime.day(), recovered.day());
503 assert_eq!(datetime.hour(), recovered.hour());
504 assert_eq!(datetime.minute(), recovered.minute());
505 assert_eq!(datetime.second(), recovered.second());
506 assert_eq!(datetime.nanosecond(), recovered.nanosecond());
507 }
508 }
509
510 #[test]
511 fn test_datetime_components() {
512 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
513
514 assert_eq!(datetime.year(), 2024);
515 assert_eq!(datetime.month(), 3);
516 assert_eq!(datetime.day(), 15);
517 assert_eq!(datetime.hour(), 14);
518 assert_eq!(datetime.minute(), 30);
519 assert_eq!(datetime.second(), 45);
520 assert_eq!(datetime.nanosecond(), 123456789);
521 }
522
523 #[test]
524 fn test_serde_roundtrip() {
525 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
526 let json = serde_json::to_string(&datetime).unwrap();
527 assert_eq!(json, "\"2024-03-15T14:30:45.123456789Z\"");
528
529 let recovered: DateTime = serde_json::from_str(&json).unwrap();
530 assert_eq!(datetime, recovered);
531 }
532}