1 use super::NaiveDateTime;
2 use crate::{Datelike, FixedOffset, LocalResult, NaiveDate, TimeDelta, Utc};
3 
4 #[test]
test_datetime_from_timestamp_millis()5 fn test_datetime_from_timestamp_millis() {
6     let valid_map = [
7         (1662921288000, "2022-09-11 18:34:48.000000000"),
8         (1662921288123, "2022-09-11 18:34:48.123000000"),
9         (1662921287890, "2022-09-11 18:34:47.890000000"),
10         (-2208936075000, "1900-01-01 14:38:45.000000000"),
11         (0, "1970-01-01 00:00:00.000000000"),
12         (119731017000, "1973-10-17 18:36:57.000000000"),
13         (1234567890000, "2009-02-13 23:31:30.000000000"),
14         (2034061609000, "2034-06-16 09:06:49.000000000"),
15     ];
16 
17     for (timestamp_millis, _formatted) in valid_map.iter().copied() {
18         let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis);
19         assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis());
20         #[cfg(feature = "alloc")]
21         assert_eq!(naive_datetime.unwrap().format("%F %T%.9f").to_string(), _formatted);
22     }
23 
24     let invalid = [i64::MAX, i64::MIN];
25 
26     for timestamp_millis in invalid.iter().copied() {
27         let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis);
28         assert!(naive_datetime.is_none());
29     }
30 
31     // Test that the result of `from_timestamp_millis` compares equal to
32     // that of `from_timestamp_opt`.
33     let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678];
34     for secs in secs_test.iter().cloned() {
35         assert_eq!(
36             NaiveDateTime::from_timestamp_millis(secs * 1000),
37             NaiveDateTime::from_timestamp_opt(secs, 0)
38         );
39     }
40 }
41 
42 #[test]
test_datetime_from_timestamp_micros()43 fn test_datetime_from_timestamp_micros() {
44     let valid_map = [
45         (1662921288000000, "2022-09-11 18:34:48.000000000"),
46         (1662921288123456, "2022-09-11 18:34:48.123456000"),
47         (1662921287890000, "2022-09-11 18:34:47.890000000"),
48         (-2208936075000000, "1900-01-01 14:38:45.000000000"),
49         (0, "1970-01-01 00:00:00.000000000"),
50         (119731017000000, "1973-10-17 18:36:57.000000000"),
51         (1234567890000000, "2009-02-13 23:31:30.000000000"),
52         (2034061609000000, "2034-06-16 09:06:49.000000000"),
53     ];
54 
55     for (timestamp_micros, _formatted) in valid_map.iter().copied() {
56         let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros);
57         assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros());
58         #[cfg(feature = "alloc")]
59         assert_eq!(naive_datetime.unwrap().format("%F %T%.9f").to_string(), _formatted);
60     }
61 
62     let invalid = [i64::MAX, i64::MIN];
63 
64     for timestamp_micros in invalid.iter().copied() {
65         let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros);
66         assert!(naive_datetime.is_none());
67     }
68 
69     // Test that the result of `from_timestamp_micros` compares equal to
70     // that of `from_timestamp_opt`.
71     let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678];
72     for secs in secs_test.iter().copied() {
73         assert_eq!(
74             NaiveDateTime::from_timestamp_micros(secs * 1_000_000),
75             NaiveDateTime::from_timestamp_opt(secs, 0)
76         );
77     }
78 }
79 
80 #[test]
test_datetime_from_timestamp_nanos()81 fn test_datetime_from_timestamp_nanos() {
82     let valid_map = [
83         (1662921288000000000, "2022-09-11 18:34:48.000000000"),
84         (1662921288123456000, "2022-09-11 18:34:48.123456000"),
85         (1662921288123456789, "2022-09-11 18:34:48.123456789"),
86         (1662921287890000000, "2022-09-11 18:34:47.890000000"),
87         (-2208936075000000000, "1900-01-01 14:38:45.000000000"),
88         (-5337182663000000000, "1800-11-15 01:15:37.000000000"),
89         (0, "1970-01-01 00:00:00.000000000"),
90         (119731017000000000, "1973-10-17 18:36:57.000000000"),
91         (1234567890000000000, "2009-02-13 23:31:30.000000000"),
92         (2034061609000000000, "2034-06-16 09:06:49.000000000"),
93     ];
94 
95     for (timestamp_nanos, _formatted) in valid_map.iter().copied() {
96         let naive_datetime = NaiveDateTime::from_timestamp_nanos(timestamp_nanos).unwrap();
97         assert_eq!(timestamp_nanos, naive_datetime.timestamp_nanos_opt().unwrap());
98         #[cfg(feature = "alloc")]
99         assert_eq!(naive_datetime.format("%F %T%.9f").to_string(), _formatted);
100     }
101 
102     const A_BILLION: i64 = 1_000_000_000;
103     // Maximum datetime in nanoseconds
104     let maximum = "2262-04-11T23:47:16.854775804";
105     let parsed: NaiveDateTime = maximum.parse().unwrap();
106     let nanos = parsed.timestamp_nanos_opt().unwrap();
107     assert_eq!(
108         NaiveDateTime::from_timestamp_nanos(nanos).unwrap(),
109         NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap()
110     );
111     // Minimum datetime in nanoseconds
112     let minimum = "1677-09-21T00:12:44.000000000";
113     let parsed: NaiveDateTime = minimum.parse().unwrap();
114     let nanos = parsed.timestamp_nanos_opt().unwrap();
115     assert_eq!(
116         NaiveDateTime::from_timestamp_nanos(nanos).unwrap(),
117         NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap()
118     );
119 
120     // Test that the result of `from_timestamp_nanos` compares equal to
121     // that of `from_timestamp_opt`.
122     let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678];
123     for secs in secs_test.iter().copied() {
124         assert_eq!(
125             NaiveDateTime::from_timestamp_nanos(secs * 1_000_000_000),
126             NaiveDateTime::from_timestamp_opt(secs, 0)
127         );
128     }
129 }
130 
131 #[test]
test_datetime_from_timestamp()132 fn test_datetime_from_timestamp() {
133     let from_timestamp = |secs| NaiveDateTime::from_timestamp_opt(secs, 0);
134     let ymdhms =
135         |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
136     assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59)));
137     assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0)));
138     assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1)));
139     assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40)));
140     assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7)));
141     assert_eq!(from_timestamp(i64::MIN), None);
142     assert_eq!(from_timestamp(i64::MAX), None);
143 }
144 
145 #[test]
test_datetime_add()146 fn test_datetime_add() {
147     fn check(
148         (y, m, d, h, n, s): (i32, u32, u32, u32, u32, u32),
149         rhs: TimeDelta,
150         result: Option<(i32, u32, u32, u32, u32, u32)>,
151     ) {
152         let lhs = NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
153         let sum = result.map(|(y, m, d, h, n, s)| {
154             NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap()
155         });
156         assert_eq!(lhs.checked_add_signed(rhs), sum);
157         assert_eq!(lhs.checked_sub_signed(-rhs), sum);
158     }
159 
160     check((2014, 5, 6, 7, 8, 9), TimeDelta::seconds(3600 + 60 + 1), Some((2014, 5, 6, 8, 9, 10)));
161     check((2014, 5, 6, 7, 8, 9), TimeDelta::seconds(-(3600 + 60 + 1)), Some((2014, 5, 6, 6, 7, 8)));
162     check((2014, 5, 6, 7, 8, 9), TimeDelta::seconds(86399), Some((2014, 5, 7, 7, 8, 8)));
163     check((2014, 5, 6, 7, 8, 9), TimeDelta::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9)));
164     check((2014, 5, 6, 7, 8, 9), TimeDelta::seconds(-86_400 * 10), Some((2014, 4, 26, 7, 8, 9)));
165     check((2014, 5, 6, 7, 8, 9), TimeDelta::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9)));
166 
167     // overflow check
168     // assumes that we have correct values for MAX/MIN_DAYS_FROM_YEAR_0 from `naive::date`.
169     // (they are private constants, but the equivalence is tested in that module.)
170     let max_days_from_year_0 =
171         NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap());
172     check((0, 1, 1, 0, 0, 0), max_days_from_year_0, Some((NaiveDate::MAX.year(), 12, 31, 0, 0, 0)));
173     check(
174         (0, 1, 1, 0, 0, 0),
175         max_days_from_year_0 + TimeDelta::seconds(86399),
176         Some((NaiveDate::MAX.year(), 12, 31, 23, 59, 59)),
177     );
178     check((0, 1, 1, 0, 0, 0), max_days_from_year_0 + TimeDelta::seconds(86_400), None);
179     check((0, 1, 1, 0, 0, 0), TimeDelta::max_value(), None);
180 
181     let min_days_from_year_0 =
182         NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap());
183     check((0, 1, 1, 0, 0, 0), min_days_from_year_0, Some((NaiveDate::MIN.year(), 1, 1, 0, 0, 0)));
184     check((0, 1, 1, 0, 0, 0), min_days_from_year_0 - TimeDelta::seconds(1), None);
185     check((0, 1, 1, 0, 0, 0), TimeDelta::min_value(), None);
186 }
187 
188 #[test]
test_datetime_sub()189 fn test_datetime_sub() {
190     let ymdhms =
191         |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
192     let since = NaiveDateTime::signed_duration_since;
193     assert_eq!(since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 9)), TimeDelta::zero());
194     assert_eq!(
195         since(ymdhms(2014, 5, 6, 7, 8, 10), ymdhms(2014, 5, 6, 7, 8, 9)),
196         TimeDelta::seconds(1)
197     );
198     assert_eq!(
199         since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)),
200         TimeDelta::seconds(-1)
201     );
202     assert_eq!(
203         since(ymdhms(2014, 5, 7, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)),
204         TimeDelta::seconds(86399)
205     );
206     assert_eq!(
207         since(ymdhms(2001, 9, 9, 1, 46, 39), ymdhms(1970, 1, 1, 0, 0, 0)),
208         TimeDelta::seconds(999_999_999)
209     );
210 }
211 
212 #[test]
test_datetime_addassignment()213 fn test_datetime_addassignment() {
214     let ymdhms =
215         |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
216     let mut date = ymdhms(2016, 10, 1, 10, 10, 10);
217     date += TimeDelta::minutes(10_000_000);
218     assert_eq!(date, ymdhms(2035, 10, 6, 20, 50, 10));
219     date += TimeDelta::days(10);
220     assert_eq!(date, ymdhms(2035, 10, 16, 20, 50, 10));
221 }
222 
223 #[test]
test_datetime_subassignment()224 fn test_datetime_subassignment() {
225     let ymdhms =
226         |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
227     let mut date = ymdhms(2016, 10, 1, 10, 10, 10);
228     date -= TimeDelta::minutes(10_000_000);
229     assert_eq!(date, ymdhms(1997, 9, 26, 23, 30, 10));
230     date -= TimeDelta::days(10);
231     assert_eq!(date, ymdhms(1997, 9, 16, 23, 30, 10));
232 }
233 
234 #[test]
test_core_duration_ops()235 fn test_core_duration_ops() {
236     use core::time::Duration;
237 
238     let mut dt = NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(11, 34, 12).unwrap();
239     let same = dt + Duration::ZERO;
240     assert_eq!(dt, same);
241 
242     dt += Duration::new(3600, 0);
243     assert_eq!(dt, NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(12, 34, 12).unwrap());
244 }
245 
246 #[test]
247 #[should_panic]
test_core_duration_max()248 fn test_core_duration_max() {
249     use core::time::Duration;
250 
251     let mut utc_dt = NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(11, 34, 12).unwrap();
252     utc_dt += Duration::MAX;
253 }
254 
255 #[test]
test_datetime_timestamp()256 fn test_datetime_timestamp() {
257     let to_timestamp = |y, m, d, h, n, s| {
258         NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().timestamp()
259     };
260     assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1);
261     assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0);
262     assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1);
263     assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000);
264     assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff);
265 }
266 
267 #[test]
test_datetime_from_str()268 fn test_datetime_from_str() {
269     // valid cases
270     let valid = [
271         "2001-02-03T04:05:06",
272         "2012-12-12T12:12:12",
273         "2015-02-18T23:16:09.153",
274         "2015-2-18T23:16:09.153",
275         "-77-02-18T23:16:09",
276         "+82701-05-6T15:9:60.898989898989",
277         "  +82701  -  05  -  6  T  15  :  9  : 60.898989898989   ",
278     ];
279     for &s in &valid {
280         eprintln!("test_parse_naivedatetime valid {:?}", s);
281         let d = match s.parse::<NaiveDateTime>() {
282             Ok(d) => d,
283             Err(e) => panic!("parsing `{}` has failed: {}", s, e),
284         };
285         let s_ = format!("{:?}", d);
286         // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same
287         let d_ = match s_.parse::<NaiveDateTime>() {
288             Ok(d) => d,
289             Err(e) => {
290                 panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
291             }
292         };
293         assert!(
294             d == d_,
295             "`{}` is parsed into `{:?}`, but reparsed result \
296              `{:?}` does not match",
297             s,
298             d,
299             d_
300         );
301     }
302 
303     // some invalid cases
304     // since `ParseErrorKind` is private, all we can do is to check if there was an error
305     let invalid = [
306         "",                              // empty
307         "x",                             // invalid / missing data
308         "15",                            // missing data
309         "15:8:9",                        // looks like a time (invalid date)
310         "15-8-9",                        // looks like a date (invalid)
311         "Fri, 09 Aug 2013 23:54:35 GMT", // valid date, wrong format
312         "Sat Jun 30 23:59:60 2012",      // valid date, wrong format
313         "1441497364.649",                // valid date, wrong format
314         "+1441497364.649",               // valid date, wrong format
315         "+1441497364",                   // valid date, wrong format
316         "2014/02/03 04:05:06",           // valid date, wrong format
317         "2015-15-15T15:15:15",           // invalid date
318         "2012-12-12T12:12:12x",          // bad timezone / trailing literal
319         "2012-12-12T12:12:12+00:00",     // unexpected timezone / trailing literal
320         "2012-12-12T12:12:12 +00:00",    // unexpected timezone / trailing literal
321         "2012-12-12T12:12:12 GMT",       // unexpected timezone / trailing literal
322         "2012-123-12T12:12:12",          // invalid month
323         "2012-12-12t12:12:12",           // bad divider 't'
324         "2012-12-12 12:12:12",           // missing divider 'T'
325         "2012-12-12T12:12:12Z",          // trailing char 'Z'
326         "+ 82701-123-12T12:12:12",       // strange year, invalid month
327         "+802701-123-12T12:12:12",       // out-of-bound year, invalid month
328     ];
329     for &s in &invalid {
330         eprintln!("test_datetime_from_str invalid {:?}", s);
331         assert!(s.parse::<NaiveDateTime>().is_err());
332     }
333 }
334 
335 #[test]
test_datetime_parse_from_str()336 fn test_datetime_parse_from_str() {
337     let ymdhms =
338         |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
339     let ymdhmsn = |y, m, d, h, n, s, nano| {
340         NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap()
341     };
342     assert_eq!(
343         NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
344         Ok(ymdhms(2014, 5, 7, 12, 34, 56))
345     ); // ignore offset
346     assert_eq!(
347         NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"),
348         Ok(ymdhms(2015, 2, 2, 0, 0, 0))
349     );
350     assert_eq!(
351         NaiveDateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"),
352         Ok(ymdhms(2013, 8, 9, 23, 54, 35))
353     );
354     assert!(NaiveDateTime::parse_from_str(
355         "Sat, 09 Aug 2013 23:54:35 GMT",
356         "%a, %d %b %Y %H:%M:%S GMT"
357     )
358     .is_err());
359     assert!(NaiveDateTime::parse_from_str("2014-5-7 12:3456", "%Y-%m-%d %H:%M:%S").is_err());
360     assert!(NaiveDateTime::parse_from_str("12:34:56", "%H:%M:%S").is_err()); // insufficient
361     assert_eq!(
362         NaiveDateTime::parse_from_str("1441497364", "%s"),
363         Ok(ymdhms(2015, 9, 5, 23, 56, 4))
364     );
365     assert_eq!(
366         NaiveDateTime::parse_from_str("1283929614.1234", "%s.%f"),
367         Ok(ymdhmsn(2010, 9, 8, 7, 6, 54, 1234))
368     );
369     assert_eq!(
370         NaiveDateTime::parse_from_str("1441497364.649", "%s%.3f"),
371         Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000))
372     );
373     assert_eq!(
374         NaiveDateTime::parse_from_str("1497854303.087654", "%s%.6f"),
375         Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000))
376     );
377     assert_eq!(
378         NaiveDateTime::parse_from_str("1437742189.918273645", "%s%.9f"),
379         Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645))
380     );
381 }
382 
383 #[test]
test_datetime_parse_from_str_with_spaces()384 fn test_datetime_parse_from_str_with_spaces() {
385     let parse_from_str = NaiveDateTime::parse_from_str;
386     let dt = NaiveDate::from_ymd_opt(2013, 8, 9).unwrap().and_hms_opt(23, 54, 35).unwrap();
387     // with varying spaces - should succeed
388     assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
389     assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S "), Ok(dt));
390     assert_eq!(parse_from_str(" Aug 09 2013  23:54:35 ", " %b %d %Y  %H:%M:%S "), Ok(dt));
391     assert_eq!(parse_from_str("  Aug 09 2013 23:54:35", "  %b %d %Y %H:%M:%S"), Ok(dt));
392     assert_eq!(parse_from_str("   Aug 09 2013 23:54:35", "   %b %d %Y %H:%M:%S"), Ok(dt));
393     assert_eq!(parse_from_str("\n\tAug 09 2013 23:54:35  ", "\n\t%b %d %Y %H:%M:%S  "), Ok(dt));
394     assert_eq!(parse_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), Ok(dt));
395     assert_eq!(parse_from_str("Aug  09 2013 23:54:35", "%b  %d %Y %H:%M:%S"), Ok(dt));
396     assert_eq!(parse_from_str("Aug    09 2013 23:54:35", "%b    %d %Y %H:%M:%S"), Ok(dt));
397     assert_eq!(parse_from_str("Aug  09 2013\t23:54:35", "%b  %d %Y\t%H:%M:%S"), Ok(dt));
398     assert_eq!(parse_from_str("Aug  09 2013\t\t23:54:35", "%b  %d %Y\t\t%H:%M:%S"), Ok(dt));
399     assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n"), Ok(dt));
400     assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt));
401     assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S "), Ok(dt));
402     assert_eq!(parse_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
403     assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n"), Ok(dt));
404     // with varying spaces - should fail
405     // leading space in data
406     assert!(parse_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
407     // trailing space in data
408     assert!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S").is_err());
409     // trailing tab in data
410     assert!(parse_from_str("Aug 09 2013 23:54:35\t", "%b %d %Y %H:%M:%S").is_err());
411     // mismatched newlines
412     assert!(parse_from_str("\nAug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
413     // trailing literal in data
414     assert!(parse_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err());
415 }
416 
417 #[test]
test_datetime_add_sub_invariant()418 fn test_datetime_add_sub_invariant() {
419     // issue #37
420     let base = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
421     let t = -946684799990000;
422     let time = base + TimeDelta::microseconds(t);
423     assert_eq!(t, time.signed_duration_since(base).num_microseconds().unwrap());
424 }
425 
426 #[test]
test_nanosecond_range()427 fn test_nanosecond_range() {
428     const A_BILLION: i64 = 1_000_000_000;
429     let maximum = "2262-04-11T23:47:16.854775804";
430     let parsed: NaiveDateTime = maximum.parse().unwrap();
431     let nanos = parsed.timestamp_nanos_opt().unwrap();
432     assert_eq!(
433         parsed,
434         NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap()
435     );
436 
437     let minimum = "1677-09-21T00:12:44.000000000";
438     let parsed: NaiveDateTime = minimum.parse().unwrap();
439     let nanos = parsed.timestamp_nanos_opt().unwrap();
440     assert_eq!(
441         parsed,
442         NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap()
443     );
444 
445     // Just beyond range
446     let maximum = "2262-04-11T23:47:16.854775804";
447     let parsed: NaiveDateTime = maximum.parse().unwrap();
448     let beyond_max = parsed + TimeDelta::milliseconds(300);
449     assert!(beyond_max.timestamp_nanos_opt().is_none());
450 
451     // Far beyond range
452     let maximum = "2262-04-11T23:47:16.854775804";
453     let parsed: NaiveDateTime = maximum.parse().unwrap();
454     let beyond_max = parsed + TimeDelta::days(365);
455     assert!(beyond_max.timestamp_nanos_opt().is_none());
456 }
457 
458 #[test]
test_and_local_timezone()459 fn test_and_local_timezone() {
460     let ndt = NaiveDate::from_ymd_opt(2022, 6, 15).unwrap().and_hms_opt(18, 59, 36).unwrap();
461     let dt_utc = ndt.and_local_timezone(Utc).unwrap();
462     assert_eq!(dt_utc.naive_local(), ndt);
463     assert_eq!(dt_utc.timezone(), Utc);
464 
465     let offset_tz = FixedOffset::west_opt(4 * 3600).unwrap();
466     let dt_offset = ndt.and_local_timezone(offset_tz).unwrap();
467     assert_eq!(dt_offset.naive_local(), ndt);
468     assert_eq!(dt_offset.timezone(), offset_tz);
469 }
470 
471 #[test]
test_and_utc()472 fn test_and_utc() {
473     let ndt = NaiveDate::from_ymd_opt(2023, 1, 30).unwrap().and_hms_opt(19, 32, 33).unwrap();
474     let dt_utc = ndt.and_utc();
475     assert_eq!(dt_utc.naive_local(), ndt);
476     assert_eq!(dt_utc.timezone(), Utc);
477 }
478 
479 #[test]
test_checked_add_offset()480 fn test_checked_add_offset() {
481     let ymdhmsm = |y, m, d, h, mn, s, mi| {
482         NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi)
483     };
484 
485     let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap();
486     // regular date
487     let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
488     assert_eq!(dt.checked_add_offset(positive_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0));
489     // leap second is preserved
490     let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
491     assert_eq!(dt.checked_add_offset(positive_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000));
492     // out of range
493     assert!(NaiveDateTime::MAX.checked_add_offset(positive_offset).is_none());
494 
495     let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap();
496     // regular date
497     let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
498     assert_eq!(dt.checked_add_offset(negative_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0));
499     // leap second is preserved
500     let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
501     assert_eq!(dt.checked_add_offset(negative_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000));
502     // out of range
503     assert!(NaiveDateTime::MIN.checked_add_offset(negative_offset).is_none());
504 }
505 
506 #[test]
test_checked_sub_offset()507 fn test_checked_sub_offset() {
508     let ymdhmsm = |y, m, d, h, mn, s, mi| {
509         NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi)
510     };
511 
512     let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap();
513     // regular date
514     let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
515     assert_eq!(dt.checked_sub_offset(positive_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0));
516     // leap second is preserved
517     let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
518     assert_eq!(dt.checked_sub_offset(positive_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000));
519     // out of range
520     assert!(NaiveDateTime::MIN.checked_sub_offset(positive_offset).is_none());
521 
522     let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap();
523     // regular date
524     let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
525     assert_eq!(dt.checked_sub_offset(negative_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0));
526     // leap second is preserved
527     let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
528     assert_eq!(dt.checked_sub_offset(negative_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000));
529     // out of range
530     assert!(NaiveDateTime::MAX.checked_sub_offset(negative_offset).is_none());
531 
532     assert_eq!(dt.checked_add_offset(positive_offset), Some(dt + positive_offset));
533     assert_eq!(dt.checked_sub_offset(positive_offset), Some(dt - positive_offset));
534 }
535 
536 #[test]
test_overflowing_add_offset()537 fn test_overflowing_add_offset() {
538     let ymdhmsm = |y, m, d, h, mn, s, mi| {
539         NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi).unwrap()
540     };
541     let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap();
542     // regular date
543     let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0);
544     assert_eq!(dt.overflowing_add_offset(positive_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0));
545     // leap second is preserved
546     let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000);
547     assert_eq!(dt.overflowing_add_offset(positive_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000));
548     // out of range
549     assert!(NaiveDateTime::MAX.overflowing_add_offset(positive_offset) > NaiveDateTime::MAX);
550 
551     let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap();
552     // regular date
553     let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0);
554     assert_eq!(dt.overflowing_add_offset(negative_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0));
555     // leap second is preserved
556     let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000);
557     assert_eq!(dt.overflowing_add_offset(negative_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000));
558     // out of range
559     assert!(NaiveDateTime::MIN.overflowing_add_offset(negative_offset) < NaiveDateTime::MIN);
560 }
561 
562 #[test]
test_and_timezone_min_max_dates()563 fn test_and_timezone_min_max_dates() {
564     for offset_hour in -23..=23 {
565         dbg!(offset_hour);
566         let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap();
567 
568         let local_max = NaiveDateTime::MAX.and_local_timezone(offset);
569         if offset_hour >= 0 {
570             assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX);
571         } else {
572             assert_eq!(local_max, LocalResult::None);
573         }
574         let local_min = NaiveDateTime::MIN.and_local_timezone(offset);
575         if offset_hour <= 0 {
576             assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN);
577         } else {
578             assert_eq!(local_min, LocalResult::None);
579         }
580     }
581 }
582 
583 #[test]
584 #[cfg(feature = "rkyv-validation")]
test_rkyv_validation()585 fn test_rkyv_validation() {
586     let dt_min = NaiveDateTime::MIN;
587     let bytes = rkyv::to_bytes::<_, 12>(&dt_min).unwrap();
588     assert_eq!(rkyv::from_bytes::<NaiveDateTime>(&bytes).unwrap(), dt_min);
589 
590     let dt_max = NaiveDateTime::MAX;
591     let bytes = rkyv::to_bytes::<_, 12>(&dt_max).unwrap();
592     assert_eq!(rkyv::from_bytes::<NaiveDateTime>(&bytes).unwrap(), dt_max);
593 }
594