1use std::fmt::{self, Display, Formatter};
5
6use serde::{
7 Deserialize, Deserializer, Serialize, Serializer,
8 de::{self, Visitor},
9};
10
11use crate::{
12 error::{TemporalKind, TypeError},
13 fragment::Fragment,
14 value::{date::Date, duration::Duration, time::Time},
15};
16
17const NANOS_PER_SECOND: u64 = 1_000_000_000;
18const NANOS_PER_MILLI: u64 = 1_000_000;
19const NANOS_PER_DAY: u64 = 86_400 * NANOS_PER_SECOND;
20
21#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
27pub struct DateTime {
28 nanos: u64,
29}
30
31impl Default for DateTime {
32 fn default() -> Self {
33 Self {
34 nanos: 0,
35 } }
37}
38
39impl DateTime {
40 pub fn new(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, nano: u32) -> Option<Self> {
43 let date = Date::new(year, month, day)?;
44 let time = Time::new(hour, min, sec, nano)?;
45
46 let days = date.to_days_since_epoch();
47 if days < 0 {
48 return None; }
50
51 let nanos = (days as u64).checked_mul(NANOS_PER_DAY)?.checked_add(time.to_nanos_since_midnight())?;
52 Some(Self {
53 nanos,
54 })
55 }
56
57 pub fn from_ymd_hms(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> Result<Self, TypeError> {
58 Self::new(year, month, day, hour, min, sec, 0).ok_or_else(|| {
59 Self::overflow_err(format!(
60 "invalid datetime: {}-{:02}-{:02} {:02}:{:02}:{:02}",
61 year, month, day, hour, min, sec
62 ))
63 })
64 }
65
66 fn overflow_err(message: impl Into<String>) -> TypeError {
67 TypeError::Temporal {
68 kind: TemporalKind::DateTimeOverflow {
69 message: message.into(),
70 },
71 message: "datetime overflow".to_string(),
72 fragment: Fragment::None,
73 }
74 }
75
76 pub fn from_nanos(nanos: u64) -> Self {
79 Self {
80 nanos,
81 }
82 }
83
84 pub fn to_nanos(&self) -> u64 {
86 self.nanos
87 }
88
89 pub fn from_timestamp(timestamp: i64) -> Result<Self, TypeError> {
90 if timestamp < 0 {
91 return Err(Self::overflow_err(format!(
92 "DateTime does not support timestamps before Unix epoch: {}",
93 timestamp
94 )));
95 }
96 let nanos = (timestamp as u64).checked_mul(NANOS_PER_SECOND).ok_or_else(|| {
97 Self::overflow_err(format!("timestamp {} overflows DateTime range", timestamp))
98 })?;
99 Ok(Self {
100 nanos,
101 })
102 }
103
104 pub fn from_timestamp_millis(millis: u64) -> Result<Self, TypeError> {
105 let nanos = millis.checked_mul(NANOS_PER_MILLI).ok_or_else(|| {
106 Self::overflow_err(format!("timestamp_millis {} overflows DateTime range", millis))
107 })?;
108 Ok(Self {
109 nanos,
110 })
111 }
112
113 pub fn from_timestamp_nanos(nanos: u128) -> Result<Self, TypeError> {
114 let nanos = u64::try_from(nanos).map_err(|_| {
115 Self::overflow_err(format!("timestamp_nanos {} overflows u64 DateTime range", nanos))
116 })?;
117 Ok(Self {
118 nanos,
119 })
120 }
121
122 pub fn timestamp(&self) -> i64 {
123 (self.nanos / NANOS_PER_SECOND) as i64
124 }
125
126 pub fn timestamp_millis(&self) -> i64 {
127 (self.nanos / NANOS_PER_MILLI) as i64
128 }
129
130 pub fn timestamp_nanos(&self) -> Result<i64, TypeError> {
131 i64::try_from(self.nanos).map_err(|_| Self::overflow_err("DateTime nanos exceeds i64::MAX"))
132 }
133
134 pub fn try_date(&self) -> Result<Date, TypeError> {
135 let days_u64 = self.nanos / NANOS_PER_DAY;
136 let days = i32::try_from(days_u64)
137 .map_err(|_| Self::overflow_err("DateTime nanos too large for date extraction"))?;
138 Date::from_days_since_epoch(days)
139 .ok_or_else(|| Self::overflow_err("DateTime days out of range for Date"))
140 }
141
142 pub fn date(&self) -> Date {
143 self.try_date().expect("DateTime nanos too large for date extraction")
144 }
145
146 pub fn time(&self) -> Time {
147 let nanos_in_day = self.nanos % NANOS_PER_DAY;
148 Time::from_nanos_since_midnight(nanos_in_day).unwrap()
149 }
150
151 pub fn to_nanos_since_epoch_u128(&self) -> u128 {
153 self.nanos as u128
154 }
155
156 pub fn year(&self) -> i32 {
157 self.date().year()
158 }
159
160 pub fn month(&self) -> u32 {
161 self.date().month()
162 }
163
164 pub fn day(&self) -> u32 {
165 self.date().day()
166 }
167
168 pub fn hour(&self) -> u32 {
169 self.time().hour()
170 }
171
172 pub fn minute(&self) -> u32 {
173 self.time().minute()
174 }
175
176 pub fn second(&self) -> u32 {
177 self.time().second()
178 }
179
180 pub fn nanosecond(&self) -> u32 {
181 self.time().nanosecond()
182 }
183
184 pub fn add_duration(&self, dur: &Duration) -> Result<Self, TypeError> {
186 let date = self.date();
187 let time = self.time();
188 let mut year = date.year();
189 let mut month = date.month() as i32;
190 let mut day = date.day();
191
192 let total_months = month + dur.get_months();
194 year += (total_months - 1).div_euclid(12);
195 month = (total_months - 1).rem_euclid(12) + 1;
196
197 let max_day = Date::days_in_month(year, month as u32);
199 if day > max_day {
200 day = max_day;
201 }
202
203 let base_date = Date::new(year, month as u32, day).ok_or_else(|| {
205 Self::overflow_err(format!(
206 "invalid date after adding duration: {}-{:02}-{:02}",
207 year, month, day
208 ))
209 })?;
210 let base_days = base_date.to_days_since_epoch() as i64 + dur.get_days() as i64;
211 let time_nanos = time.to_nanos_since_midnight() as i64 + dur.get_nanos();
212
213 let total_nanos = base_days as i128 * 86_400_000_000_000i128 + time_nanos as i128;
214
215 if total_nanos < 0 {
216 return Err(Self::overflow_err("result is before Unix epoch"));
217 }
218
219 let nanos =
220 u64::try_from(total_nanos).map_err(|_| Self::overflow_err("result exceeds DateTime range"))?;
221 Ok(Self {
222 nanos,
223 })
224 }
225}
226
227impl Display for DateTime {
228 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
229 let date = self.date();
230 let time = self.time();
231
232 write!(f, "{}T{}Z", date, time)
234 }
235}
236
237impl Serialize for DateTime {
239 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
240 where
241 S: Serializer,
242 {
243 serializer.serialize_str(&self.to_string())
244 }
245}
246
247struct DateTimeVisitor;
248
249impl<'de> Visitor<'de> for DateTimeVisitor {
250 type Value = DateTime;
251
252 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
253 formatter.write_str("a datetime in ISO 8601 format (YYYY-MM-DDTHH:MM:SS[.nnnnnnnnn]Z)")
254 }
255
256 fn visit_str<E>(self, value: &str) -> Result<DateTime, E>
257 where
258 E: de::Error,
259 {
260 let value = value.strip_suffix('Z').unwrap_or(value);
264
265 let parts: Vec<&str> = value.split('T').collect();
267 if parts.len() != 2 {
268 return Err(E::custom(format!("invalid datetime format: {}", value)));
269 }
270
271 let date_parts: Vec<&str> = parts[0].split('-').collect();
273 if date_parts.len() != 3 {
274 return Err(E::custom(format!("invalid date format: {}", parts[0])));
275 }
276
277 let (year_str, month_str, day_str) = (date_parts[0], date_parts[1], date_parts[2]);
278
279 let year = year_str.parse::<i32>().map_err(|_| E::custom(format!("invalid year: {}", year_str)))?;
280 if year < 1970 {
281 return Err(E::custom(format!("DateTime does not support pre-epoch years: {}", year)));
282 }
283 let month = month_str.parse::<u32>().map_err(|_| E::custom(format!("invalid month: {}", month_str)))?;
284 let day = day_str.parse::<u32>().map_err(|_| E::custom(format!("invalid day: {}", day_str)))?;
285
286 let (time_part, nano_part) = if let Some(dot_pos) = parts[1].find('.') {
288 (&parts[1][..dot_pos], Some(&parts[1][dot_pos + 1..]))
289 } else {
290 (parts[1], None)
291 };
292
293 let time_parts: Vec<&str> = time_part.split(':').collect();
294 if time_parts.len() != 3 {
295 return Err(E::custom(format!("invalid time format: {}", parts[1])));
296 }
297
298 let hour = time_parts[0]
299 .parse::<u32>()
300 .map_err(|_| E::custom(format!("invalid hour: {}", time_parts[0])))?;
301 let minute = time_parts[1]
302 .parse::<u32>()
303 .map_err(|_| E::custom(format!("invalid minute: {}", time_parts[1])))?;
304 let second = time_parts[2]
305 .parse::<u32>()
306 .map_err(|_| E::custom(format!("invalid second: {}", time_parts[2])))?;
307
308 let nano = if let Some(nano_str) = nano_part {
309 let padded = if nano_str.len() < 9 {
311 format!("{:0<9}", nano_str)
312 } else {
313 nano_str[..9].to_string()
314 };
315 padded.parse::<u32>().map_err(|_| E::custom(format!("invalid nanoseconds: {}", nano_str)))?
316 } else {
317 0
318 };
319
320 DateTime::new(year, month, day, hour, minute, second, nano).ok_or_else(|| {
321 E::custom(format!(
322 "invalid datetime: {}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}Z",
323 year, month, day, hour, minute, second, nano
324 ))
325 })
326 }
327}
328
329impl<'de> Deserialize<'de> for DateTime {
330 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
331 where
332 D: Deserializer<'de>,
333 {
334 deserializer.deserialize_str(DateTimeVisitor)
335 }
336}
337
338#[cfg(test)]
339pub mod tests {
340 use std::fmt::Debug;
341
342 use serde_json::{from_str, to_string};
343
344 use crate::{
345 error::{TemporalKind, TypeError},
346 value::{datetime::DateTime, duration::Duration},
347 };
348
349 #[test]
350 fn test_datetime_display_standard_format() {
351 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
352 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123456789Z");
353
354 let datetime = DateTime::new(2000, 1, 1, 0, 0, 0, 0).unwrap();
355 assert_eq!(format!("{}", datetime), "2000-01-01T00:00:00.000000000Z");
356
357 let datetime = DateTime::new(1999, 12, 31, 23, 59, 59, 999999999).unwrap();
358 assert_eq!(format!("{}", datetime), "1999-12-31T23:59:59.999999999Z");
359 }
360
361 #[test]
362 fn test_datetime_display_millisecond_precision() {
363 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123000000).unwrap();
364 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123000000Z");
365
366 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 001000000).unwrap();
367 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.001000000Z");
368
369 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 999000000).unwrap();
370 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.999000000Z");
371 }
372
373 #[test]
374 fn test_datetime_display_microsecond_precision() {
375 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456000).unwrap();
376 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123456000Z");
377
378 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 000001000).unwrap();
379 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.000001000Z");
380
381 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 999999000).unwrap();
382 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.999999000Z");
383 }
384
385 #[test]
386 fn test_datetime_display_nanosecond_precision() {
387 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
388 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.123456789Z");
389
390 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 000000001).unwrap();
391 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.000000001Z");
392
393 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 999999999).unwrap();
394 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.999999999Z");
395 }
396
397 #[test]
398 fn test_datetime_display_zero_fractional_seconds() {
399 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 0).unwrap();
400 assert_eq!(format!("{}", datetime), "2024-03-15T14:30:45.000000000Z");
401
402 let datetime = DateTime::new(2024, 3, 15, 0, 0, 0, 0).unwrap();
403 assert_eq!(format!("{}", datetime), "2024-03-15T00:00:00.000000000Z");
404 }
405
406 #[test]
407 fn test_datetime_display_edge_times() {
408 let datetime = DateTime::new(2024, 3, 15, 0, 0, 0, 0).unwrap();
410 assert_eq!(format!("{}", datetime), "2024-03-15T00:00:00.000000000Z");
411
412 let datetime = DateTime::new(2024, 3, 15, 23, 59, 59, 999999999).unwrap();
414 assert_eq!(format!("{}", datetime), "2024-03-15T23:59:59.999999999Z");
415
416 let datetime = DateTime::new(2024, 3, 15, 12, 0, 0, 0).unwrap();
418 assert_eq!(format!("{}", datetime), "2024-03-15T12:00:00.000000000Z");
419 }
420
421 #[test]
422 fn test_datetime_display_unix_epoch() {
423 let datetime = DateTime::new(1970, 1, 1, 0, 0, 0, 0).unwrap();
424 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
425
426 let datetime = DateTime::new(1970, 1, 1, 0, 0, 1, 0).unwrap();
427 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:01.000000000Z");
428 }
429
430 #[test]
431 fn test_datetime_display_leap_year() {
432 let datetime = DateTime::new(2024, 2, 29, 12, 30, 45, 123456789).unwrap();
433 assert_eq!(format!("{}", datetime), "2024-02-29T12:30:45.123456789Z");
434
435 let datetime = DateTime::new(2000, 2, 29, 0, 0, 0, 0).unwrap();
436 assert_eq!(format!("{}", datetime), "2000-02-29T00:00:00.000000000Z");
437 }
438
439 #[test]
440 fn test_datetime_display_boundary_dates() {
441 let datetime = DateTime::new(2000, 1, 1, 0, 0, 0, 0).unwrap();
443 assert_eq!(format!("{}", datetime), "2000-01-01T00:00:00.000000000Z");
444
445 let datetime = DateTime::new(2100, 1, 1, 0, 0, 0, 0).unwrap();
446 assert_eq!(format!("{}", datetime), "2100-01-01T00:00:00.000000000Z");
447
448 let datetime = DateTime::new(2554, 1, 1, 0, 0, 0, 0).unwrap();
450 assert_eq!(format!("{}", datetime), "2554-01-01T00:00:00.000000000Z");
451
452 assert!(DateTime::new(9999, 12, 31, 23, 59, 59, 999999999).is_none());
454 }
455
456 #[test]
457 fn test_datetime_rejects_pre_epoch() {
458 assert!(DateTime::new(1, 1, 1, 0, 0, 0, 0).is_none());
460
461 assert!(DateTime::new(1900, 1, 1, 0, 0, 0, 0).is_none());
463
464 assert!(DateTime::new(1969, 12, 31, 23, 59, 59, 999999999).is_none());
466
467 assert!(DateTime::from_timestamp(-1).is_err());
469 }
470
471 #[test]
472 fn test_datetime_display_default() {
473 let datetime = DateTime::default();
474 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
475 }
476
477 #[test]
478 fn test_datetime_display_all_hours() {
479 for hour in 0..24 {
480 let datetime = DateTime::new(2024, 3, 15, hour, 30, 45, 123456789).unwrap();
481 let expected = format!("2024-03-15T{:02}:30:45.123456789Z", hour);
482 assert_eq!(format!("{}", datetime), expected);
483 }
484 }
485
486 #[test]
487 fn test_datetime_display_all_minutes() {
488 for minute in 0..60 {
489 let datetime = DateTime::new(2024, 3, 15, 14, minute, 45, 123456789).unwrap();
490 let expected = format!("2024-03-15T14:{:02}:45.123456789Z", minute);
491 assert_eq!(format!("{}", datetime), expected);
492 }
493 }
494
495 #[test]
496 fn test_datetime_display_all_seconds() {
497 for second in 0..60 {
498 let datetime = DateTime::new(2024, 3, 15, 14, 30, second, 123456789).unwrap();
499 let expected = format!("2024-03-15T14:30:{:02}.123456789Z", second);
500 assert_eq!(format!("{}", datetime), expected);
501 }
502 }
503
504 #[test]
505 fn test_datetime_display_from_timestamp() {
506 let datetime = DateTime::from_timestamp(0).unwrap();
507 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
508
509 let datetime = DateTime::from_timestamp(1234567890).unwrap();
510 assert_eq!(format!("{}", datetime), "2009-02-13T23:31:30.000000000Z");
511 }
512
513 #[test]
514 fn test_datetime_display_from_timestamp_millis() {
515 let datetime = DateTime::from_timestamp_millis(1234567890123).unwrap();
516 assert_eq!(format!("{}", datetime), "2009-02-13T23:31:30.123000000Z");
517
518 let datetime = DateTime::from_timestamp_millis(0).unwrap();
519 assert_eq!(format!("{}", datetime), "1970-01-01T00:00:00.000000000Z");
520 }
521
522 #[test]
523 fn test_datetime_from_nanos_roundtrip() {
524 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
525 let nanos = datetime.to_nanos();
526 let recovered = DateTime::from_nanos(nanos);
527 assert_eq!(datetime, recovered);
528 }
529
530 #[test]
531 fn test_datetime_roundtrip() {
532 let test_cases = [
533 (1970, 1, 1, 0, 0, 0, 0u32),
534 (2024, 3, 15, 14, 30, 45, 123456789),
535 (2000, 2, 29, 23, 59, 59, 999999999),
536 ];
537
538 for (y, m, d, h, min, s, n) in test_cases {
539 let datetime = DateTime::new(y, m, d, h, min, s, n).unwrap();
540 let nanos = datetime.to_nanos();
541 let recovered = DateTime::from_nanos(nanos);
542
543 assert_eq!(datetime.year(), recovered.year());
544 assert_eq!(datetime.month(), recovered.month());
545 assert_eq!(datetime.day(), recovered.day());
546 assert_eq!(datetime.hour(), recovered.hour());
547 assert_eq!(datetime.minute(), recovered.minute());
548 assert_eq!(datetime.second(), recovered.second());
549 assert_eq!(datetime.nanosecond(), recovered.nanosecond());
550 }
551 }
552
553 #[test]
554 fn test_datetime_components() {
555 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
556
557 assert_eq!(datetime.year(), 2024);
558 assert_eq!(datetime.month(), 3);
559 assert_eq!(datetime.day(), 15);
560 assert_eq!(datetime.hour(), 14);
561 assert_eq!(datetime.minute(), 30);
562 assert_eq!(datetime.second(), 45);
563 assert_eq!(datetime.nanosecond(), 123456789);
564 }
565
566 #[test]
567 fn test_serde_roundtrip() {
568 let datetime = DateTime::new(2024, 3, 15, 14, 30, 45, 123456789).unwrap();
569 let json = to_string(&datetime).unwrap();
570 assert_eq!(json, "\"2024-03-15T14:30:45.123456789Z\"");
571
572 let recovered: DateTime = from_str(&json).unwrap();
573 assert_eq!(datetime, recovered);
574 }
575
576 fn assert_datetime_overflow<T: Debug>(result: Result<T, TypeError>) {
577 let err = result.expect_err("expected DateTimeOverflow error");
578 match err {
579 TypeError::Temporal {
580 kind: TemporalKind::DateTimeOverflow {
581 ..
582 },
583 ..
584 } => {}
585 other => panic!("expected DateTimeOverflow, got: {:?}", other),
586 }
587 }
588
589 #[test]
590 fn test_from_timestamp_nanos_overflow() {
591 let huge: u128 = u64::MAX as u128 + 1;
592 assert_datetime_overflow(DateTime::from_timestamp_nanos(huge));
593 }
594
595 #[test]
596 fn test_from_timestamp_nanos_max_u64_ok() {
597 let dt = DateTime::from_timestamp_nanos(u64::MAX as u128).unwrap();
598 assert_eq!(dt.to_nanos(), u64::MAX);
599 }
600
601 #[test]
602 fn test_from_timestamp_large_value_overflow() {
603 assert_datetime_overflow(DateTime::from_timestamp(i64::MAX));
604 }
605
606 #[test]
607 fn test_from_timestamp_negative_overflow() {
608 assert_datetime_overflow(DateTime::from_timestamp(-1));
609 }
610
611 #[test]
612 fn test_from_timestamp_millis_overflow() {
613 assert_datetime_overflow(DateTime::from_timestamp_millis(u64::MAX));
614 }
615
616 #[test]
617 fn test_from_timestamp_millis_boundary_ok() {
618 let dt = DateTime::from_timestamp_millis(1_700_000_000_000).unwrap();
619 assert!(dt.to_nanos() > 0);
620 }
621
622 #[test]
623 fn test_timestamp_nanos_large_value_returns_err() {
624 let dt = DateTime::from_nanos(i64::MAX as u64 + 1);
625 assert_datetime_overflow(dt.timestamp_nanos());
626 }
627
628 #[test]
629 fn test_timestamp_nanos_within_range_ok() {
630 let dt = DateTime::from_nanos(i64::MAX as u64);
631 assert_eq!(dt.timestamp_nanos().unwrap(), i64::MAX);
632 }
633
634 #[test]
635 fn test_try_date_max_nanos_ok() {
636 let dt = DateTime::from_nanos(u64::MAX);
638 let date = dt.try_date().unwrap();
639 assert!(date.year() > 2500);
640 }
641
642 #[test]
643 fn test_add_duration_overflow() {
644 let dt = DateTime::from_nanos(u64::MAX - 1);
645 let dur = Duration::from_days(1).unwrap();
646 assert_datetime_overflow(dt.add_duration(&dur));
647 }
648
649 #[test]
650 fn test_add_duration_before_epoch() {
651 let dt = DateTime::new(1970, 1, 1, 0, 0, 0, 0).unwrap();
652 let dur = Duration::from_seconds(-1).unwrap();
653 assert_datetime_overflow(dt.add_duration(&dur));
654 }
655
656 #[test]
657 fn test_add_duration_negative_nanos_borrows_from_days() {
658 let dt = DateTime::new(2024, 3, 15, 0, 0, 30, 0).unwrap();
659 let dur = Duration::from_seconds(-60).unwrap();
660 let result = dt.add_duration(&dur).unwrap();
661 assert_eq!(result.year(), 2024);
662 assert_eq!(result.month(), 3);
663 assert_eq!(result.day(), 14);
664 assert_eq!(result.hour(), 23);
665 assert_eq!(result.minute(), 59);
666 assert_eq!(result.second(), 30);
667 }
668
669 #[test]
670 fn test_add_duration_nanos_overflow_into_next_day() {
671 let dt = DateTime::new(2024, 3, 15, 23, 59, 30, 0).unwrap();
672 let dur = Duration::from_seconds(60).unwrap();
673 let result = dt.add_duration(&dur).unwrap();
674 assert_eq!(result.year(), 2024);
675 assert_eq!(result.month(), 3);
676 assert_eq!(result.day(), 16);
677 assert_eq!(result.hour(), 0);
678 assert_eq!(result.minute(), 0);
679 assert_eq!(result.second(), 30);
680 }
681}