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