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