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