1 use super::{Days, Months, NaiveDate, MAX_YEAR, MIN_YEAR};
2 use crate::naive::internals::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
3 use crate::{Datelike, TimeDelta, Weekday};
4
5 // as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`,
6 // we use a separate run-time test.
7 #[test]
test_date_bounds()8 fn test_date_bounds() {
9 let calculated_min = NaiveDate::from_ymd_opt(MIN_YEAR, 1, 1).unwrap();
10 let calculated_max = NaiveDate::from_ymd_opt(MAX_YEAR, 12, 31).unwrap();
11 assert!(
12 NaiveDate::MIN == calculated_min,
13 "`NaiveDate::MIN` should have year flag {:?}",
14 calculated_min.year_flags()
15 );
16 assert!(
17 NaiveDate::MAX == calculated_max,
18 "`NaiveDate::MAX` should have year flag {:?} and ordinal {}",
19 calculated_max.year_flags(),
20 calculated_max.ordinal()
21 );
22
23 // let's also check that the entire range do not exceed 2^44 seconds
24 // (sometimes used for bounding `TimeDelta` against overflow)
25 let maxsecs = NaiveDate::MAX.signed_duration_since(NaiveDate::MIN).num_seconds();
26 let maxsecs = maxsecs + 86401; // also take care of DateTime
27 assert!(
28 maxsecs < (1 << MAX_BITS),
29 "The entire `NaiveDate` range somehow exceeds 2^{} seconds",
30 MAX_BITS
31 );
32
33 const BEFORE_MIN: NaiveDate = NaiveDate::BEFORE_MIN;
34 assert_eq!(BEFORE_MIN.year_flags(), YearFlags::from_year(BEFORE_MIN.year()));
35 assert_eq!((BEFORE_MIN.month(), BEFORE_MIN.day()), (12, 31));
36
37 const AFTER_MAX: NaiveDate = NaiveDate::AFTER_MAX;
38 assert_eq!(AFTER_MAX.year_flags(), YearFlags::from_year(AFTER_MAX.year()));
39 assert_eq!((AFTER_MAX.month(), AFTER_MAX.day()), (1, 1));
40 }
41
42 #[test]
diff_months()43 fn diff_months() {
44 // identity
45 assert_eq!(
46 NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(0)),
47 Some(NaiveDate::from_ymd_opt(2022, 8, 3).unwrap())
48 );
49
50 // add with months exceeding `i32::MAX`
51 assert_eq!(
52 NaiveDate::from_ymd_opt(2022, 8, 3)
53 .unwrap()
54 .checked_add_months(Months::new(i32::MAX as u32 + 1)),
55 None
56 );
57
58 // sub with months exceeding `i32::MIN`
59 assert_eq!(
60 NaiveDate::from_ymd_opt(2022, 8, 3)
61 .unwrap()
62 .checked_sub_months(Months::new(i32::MIN.unsigned_abs() + 1)),
63 None
64 );
65
66 // add overflowing year
67 assert_eq!(NaiveDate::MAX.checked_add_months(Months::new(1)), None);
68
69 // add underflowing year
70 assert_eq!(NaiveDate::MIN.checked_sub_months(Months::new(1)), None);
71
72 // sub crossing year 0 boundary
73 assert_eq!(
74 NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(2050 * 12)),
75 Some(NaiveDate::from_ymd_opt(-28, 8, 3).unwrap())
76 );
77
78 // add crossing year boundary
79 assert_eq!(
80 NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(6)),
81 Some(NaiveDate::from_ymd_opt(2023, 2, 3).unwrap())
82 );
83
84 // sub crossing year boundary
85 assert_eq!(
86 NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(10)),
87 Some(NaiveDate::from_ymd_opt(2021, 10, 3).unwrap())
88 );
89
90 // add clamping day, non-leap year
91 assert_eq!(
92 NaiveDate::from_ymd_opt(2022, 1, 29).unwrap().checked_add_months(Months::new(1)),
93 Some(NaiveDate::from_ymd_opt(2022, 2, 28).unwrap())
94 );
95
96 // add to leap day
97 assert_eq!(
98 NaiveDate::from_ymd_opt(2022, 10, 29).unwrap().checked_add_months(Months::new(16)),
99 Some(NaiveDate::from_ymd_opt(2024, 2, 29).unwrap())
100 );
101
102 // add into december
103 assert_eq!(
104 NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_add_months(Months::new(2)),
105 Some(NaiveDate::from_ymd_opt(2022, 12, 31).unwrap())
106 );
107
108 // sub into december
109 assert_eq!(
110 NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_sub_months(Months::new(10)),
111 Some(NaiveDate::from_ymd_opt(2021, 12, 31).unwrap())
112 );
113
114 // add into january
115 assert_eq!(
116 NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(5)),
117 Some(NaiveDate::from_ymd_opt(2023, 1, 3).unwrap())
118 );
119
120 // sub into january
121 assert_eq!(
122 NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(7)),
123 Some(NaiveDate::from_ymd_opt(2022, 1, 3).unwrap())
124 );
125 }
126
127 #[test]
test_readme_doomsday()128 fn test_readme_doomsday() {
129 for y in NaiveDate::MIN.year()..=NaiveDate::MAX.year() {
130 // even months
131 let d4 = NaiveDate::from_ymd_opt(y, 4, 4).unwrap();
132 let d6 = NaiveDate::from_ymd_opt(y, 6, 6).unwrap();
133 let d8 = NaiveDate::from_ymd_opt(y, 8, 8).unwrap();
134 let d10 = NaiveDate::from_ymd_opt(y, 10, 10).unwrap();
135 let d12 = NaiveDate::from_ymd_opt(y, 12, 12).unwrap();
136
137 // nine to five, seven-eleven
138 let d59 = NaiveDate::from_ymd_opt(y, 5, 9).unwrap();
139 let d95 = NaiveDate::from_ymd_opt(y, 9, 5).unwrap();
140 let d711 = NaiveDate::from_ymd_opt(y, 7, 11).unwrap();
141 let d117 = NaiveDate::from_ymd_opt(y, 11, 7).unwrap();
142
143 // "March 0"
144 let d30 = NaiveDate::from_ymd_opt(y, 3, 1).unwrap().pred_opt().unwrap();
145
146 let weekday = d30.weekday();
147 let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117];
148 assert!(other_dates.iter().all(|d| d.weekday() == weekday));
149 }
150 }
151
152 #[test]
test_date_from_ymd()153 fn test_date_from_ymd() {
154 let from_ymd = NaiveDate::from_ymd_opt;
155
156 assert!(from_ymd(2012, 0, 1).is_none());
157 assert!(from_ymd(2012, 1, 1).is_some());
158 assert!(from_ymd(2012, 2, 29).is_some());
159 assert!(from_ymd(2014, 2, 29).is_none());
160 assert!(from_ymd(2014, 3, 0).is_none());
161 assert!(from_ymd(2014, 3, 1).is_some());
162 assert!(from_ymd(2014, 3, 31).is_some());
163 assert!(from_ymd(2014, 3, 32).is_none());
164 assert!(from_ymd(2014, 12, 31).is_some());
165 assert!(from_ymd(2014, 13, 1).is_none());
166 }
167
168 #[test]
test_date_from_yo()169 fn test_date_from_yo() {
170 let from_yo = NaiveDate::from_yo_opt;
171 let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
172
173 assert_eq!(from_yo(2012, 0), None);
174 assert_eq!(from_yo(2012, 1), Some(ymd(2012, 1, 1)));
175 assert_eq!(from_yo(2012, 2), Some(ymd(2012, 1, 2)));
176 assert_eq!(from_yo(2012, 32), Some(ymd(2012, 2, 1)));
177 assert_eq!(from_yo(2012, 60), Some(ymd(2012, 2, 29)));
178 assert_eq!(from_yo(2012, 61), Some(ymd(2012, 3, 1)));
179 assert_eq!(from_yo(2012, 100), Some(ymd(2012, 4, 9)));
180 assert_eq!(from_yo(2012, 200), Some(ymd(2012, 7, 18)));
181 assert_eq!(from_yo(2012, 300), Some(ymd(2012, 10, 26)));
182 assert_eq!(from_yo(2012, 366), Some(ymd(2012, 12, 31)));
183 assert_eq!(from_yo(2012, 367), None);
184 assert_eq!(from_yo(2012, 1 << 28 | 60), None);
185
186 assert_eq!(from_yo(2014, 0), None);
187 assert_eq!(from_yo(2014, 1), Some(ymd(2014, 1, 1)));
188 assert_eq!(from_yo(2014, 2), Some(ymd(2014, 1, 2)));
189 assert_eq!(from_yo(2014, 32), Some(ymd(2014, 2, 1)));
190 assert_eq!(from_yo(2014, 59), Some(ymd(2014, 2, 28)));
191 assert_eq!(from_yo(2014, 60), Some(ymd(2014, 3, 1)));
192 assert_eq!(from_yo(2014, 100), Some(ymd(2014, 4, 10)));
193 assert_eq!(from_yo(2014, 200), Some(ymd(2014, 7, 19)));
194 assert_eq!(from_yo(2014, 300), Some(ymd(2014, 10, 27)));
195 assert_eq!(from_yo(2014, 365), Some(ymd(2014, 12, 31)));
196 assert_eq!(from_yo(2014, 366), None);
197 }
198
199 #[test]
test_date_from_isoywd()200 fn test_date_from_isoywd() {
201 let from_isoywd = NaiveDate::from_isoywd_opt;
202 let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
203
204 assert_eq!(from_isoywd(2004, 0, Weekday::Sun), None);
205 assert_eq!(from_isoywd(2004, 1, Weekday::Mon), Some(ymd(2003, 12, 29)));
206 assert_eq!(from_isoywd(2004, 1, Weekday::Sun), Some(ymd(2004, 1, 4)));
207 assert_eq!(from_isoywd(2004, 2, Weekday::Mon), Some(ymd(2004, 1, 5)));
208 assert_eq!(from_isoywd(2004, 2, Weekday::Sun), Some(ymd(2004, 1, 11)));
209 assert_eq!(from_isoywd(2004, 52, Weekday::Mon), Some(ymd(2004, 12, 20)));
210 assert_eq!(from_isoywd(2004, 52, Weekday::Sun), Some(ymd(2004, 12, 26)));
211 assert_eq!(from_isoywd(2004, 53, Weekday::Mon), Some(ymd(2004, 12, 27)));
212 assert_eq!(from_isoywd(2004, 53, Weekday::Sun), Some(ymd(2005, 1, 2)));
213 assert_eq!(from_isoywd(2004, 54, Weekday::Mon), None);
214
215 assert_eq!(from_isoywd(2011, 0, Weekday::Sun), None);
216 assert_eq!(from_isoywd(2011, 1, Weekday::Mon), Some(ymd(2011, 1, 3)));
217 assert_eq!(from_isoywd(2011, 1, Weekday::Sun), Some(ymd(2011, 1, 9)));
218 assert_eq!(from_isoywd(2011, 2, Weekday::Mon), Some(ymd(2011, 1, 10)));
219 assert_eq!(from_isoywd(2011, 2, Weekday::Sun), Some(ymd(2011, 1, 16)));
220
221 assert_eq!(from_isoywd(2018, 51, Weekday::Mon), Some(ymd(2018, 12, 17)));
222 assert_eq!(from_isoywd(2018, 51, Weekday::Sun), Some(ymd(2018, 12, 23)));
223 assert_eq!(from_isoywd(2018, 52, Weekday::Mon), Some(ymd(2018, 12, 24)));
224 assert_eq!(from_isoywd(2018, 52, Weekday::Sun), Some(ymd(2018, 12, 30)));
225 assert_eq!(from_isoywd(2018, 53, Weekday::Mon), None);
226 }
227
228 #[test]
test_date_from_isoywd_and_iso_week()229 fn test_date_from_isoywd_and_iso_week() {
230 for year in 2000..2401 {
231 for week in 1..54 {
232 for &weekday in [
233 Weekday::Mon,
234 Weekday::Tue,
235 Weekday::Wed,
236 Weekday::Thu,
237 Weekday::Fri,
238 Weekday::Sat,
239 Weekday::Sun,
240 ]
241 .iter()
242 {
243 let d = NaiveDate::from_isoywd_opt(year, week, weekday);
244 if let Some(d) = d {
245 assert_eq!(d.weekday(), weekday);
246 let w = d.iso_week();
247 assert_eq!(w.year(), year);
248 assert_eq!(w.week(), week);
249 }
250 }
251 }
252 }
253
254 for year in 2000..2401 {
255 for month in 1..13 {
256 for day in 1..32 {
257 let d = NaiveDate::from_ymd_opt(year, month, day);
258 if let Some(d) = d {
259 let w = d.iso_week();
260 let d_ = NaiveDate::from_isoywd_opt(w.year(), w.week(), d.weekday());
261 assert_eq!(d, d_.unwrap());
262 }
263 }
264 }
265 }
266 }
267
268 #[test]
test_date_from_num_days_from_ce()269 fn test_date_from_num_days_from_ce() {
270 let from_ndays_from_ce = NaiveDate::from_num_days_from_ce_opt;
271 assert_eq!(from_ndays_from_ce(1), Some(NaiveDate::from_ymd_opt(1, 1, 1).unwrap()));
272 assert_eq!(from_ndays_from_ce(2), Some(NaiveDate::from_ymd_opt(1, 1, 2).unwrap()));
273 assert_eq!(from_ndays_from_ce(31), Some(NaiveDate::from_ymd_opt(1, 1, 31).unwrap()));
274 assert_eq!(from_ndays_from_ce(32), Some(NaiveDate::from_ymd_opt(1, 2, 1).unwrap()));
275 assert_eq!(from_ndays_from_ce(59), Some(NaiveDate::from_ymd_opt(1, 2, 28).unwrap()));
276 assert_eq!(from_ndays_from_ce(60), Some(NaiveDate::from_ymd_opt(1, 3, 1).unwrap()));
277 assert_eq!(from_ndays_from_ce(365), Some(NaiveDate::from_ymd_opt(1, 12, 31).unwrap()));
278 assert_eq!(from_ndays_from_ce(365 + 1), Some(NaiveDate::from_ymd_opt(2, 1, 1).unwrap()));
279 assert_eq!(from_ndays_from_ce(365 * 2 + 1), Some(NaiveDate::from_ymd_opt(3, 1, 1).unwrap()));
280 assert_eq!(from_ndays_from_ce(365 * 3 + 1), Some(NaiveDate::from_ymd_opt(4, 1, 1).unwrap()));
281 assert_eq!(from_ndays_from_ce(365 * 4 + 2), Some(NaiveDate::from_ymd_opt(5, 1, 1).unwrap()));
282 assert_eq!(from_ndays_from_ce(146097 + 1), Some(NaiveDate::from_ymd_opt(401, 1, 1).unwrap()));
283 assert_eq!(
284 from_ndays_from_ce(146097 * 5 + 1),
285 Some(NaiveDate::from_ymd_opt(2001, 1, 1).unwrap())
286 );
287 assert_eq!(from_ndays_from_ce(719163), Some(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()));
288 assert_eq!(from_ndays_from_ce(0), Some(NaiveDate::from_ymd_opt(0, 12, 31).unwrap())); // 1 BCE
289 assert_eq!(from_ndays_from_ce(-365), Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()));
290 assert_eq!(from_ndays_from_ce(-366), Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap())); // 2 BCE
291
292 for days in (-9999..10001).map(|x| x * 100) {
293 assert_eq!(from_ndays_from_ce(days).map(|d| d.num_days_from_ce()), Some(days));
294 }
295
296 assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce()), Some(NaiveDate::MIN));
297 assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce() - 1), None);
298 assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce()), Some(NaiveDate::MAX));
299 assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce() + 1), None);
300
301 assert_eq!(from_ndays_from_ce(i32::MIN), None);
302 assert_eq!(from_ndays_from_ce(i32::MAX), None);
303 }
304
305 #[test]
test_date_from_weekday_of_month_opt()306 fn test_date_from_weekday_of_month_opt() {
307 let ymwd = NaiveDate::from_weekday_of_month_opt;
308 assert_eq!(ymwd(2018, 8, Weekday::Tue, 0), None);
309 assert_eq!(ymwd(2018, 8, Weekday::Wed, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 1).unwrap()));
310 assert_eq!(ymwd(2018, 8, Weekday::Thu, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 2).unwrap()));
311 assert_eq!(ymwd(2018, 8, Weekday::Sun, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 5).unwrap()));
312 assert_eq!(ymwd(2018, 8, Weekday::Mon, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 6).unwrap()));
313 assert_eq!(ymwd(2018, 8, Weekday::Tue, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 7).unwrap()));
314 assert_eq!(ymwd(2018, 8, Weekday::Wed, 2), Some(NaiveDate::from_ymd_opt(2018, 8, 8).unwrap()));
315 assert_eq!(ymwd(2018, 8, Weekday::Sun, 2), Some(NaiveDate::from_ymd_opt(2018, 8, 12).unwrap()));
316 assert_eq!(ymwd(2018, 8, Weekday::Thu, 3), Some(NaiveDate::from_ymd_opt(2018, 8, 16).unwrap()));
317 assert_eq!(ymwd(2018, 8, Weekday::Thu, 4), Some(NaiveDate::from_ymd_opt(2018, 8, 23).unwrap()));
318 assert_eq!(ymwd(2018, 8, Weekday::Thu, 5), Some(NaiveDate::from_ymd_opt(2018, 8, 30).unwrap()));
319 assert_eq!(ymwd(2018, 8, Weekday::Fri, 5), Some(NaiveDate::from_ymd_opt(2018, 8, 31).unwrap()));
320 assert_eq!(ymwd(2018, 8, Weekday::Sat, 5), None);
321 }
322
323 #[test]
test_date_fields()324 fn test_date_fields() {
325 fn check(year: i32, month: u32, day: u32, ordinal: u32) {
326 let d1 = NaiveDate::from_ymd_opt(year, month, day).unwrap();
327 assert_eq!(d1.year(), year);
328 assert_eq!(d1.month(), month);
329 assert_eq!(d1.day(), day);
330 assert_eq!(d1.ordinal(), ordinal);
331
332 let d2 = NaiveDate::from_yo_opt(year, ordinal).unwrap();
333 assert_eq!(d2.year(), year);
334 assert_eq!(d2.month(), month);
335 assert_eq!(d2.day(), day);
336 assert_eq!(d2.ordinal(), ordinal);
337
338 assert_eq!(d1, d2);
339 }
340
341 check(2012, 1, 1, 1);
342 check(2012, 1, 2, 2);
343 check(2012, 2, 1, 32);
344 check(2012, 2, 29, 60);
345 check(2012, 3, 1, 61);
346 check(2012, 4, 9, 100);
347 check(2012, 7, 18, 200);
348 check(2012, 10, 26, 300);
349 check(2012, 12, 31, 366);
350
351 check(2014, 1, 1, 1);
352 check(2014, 1, 2, 2);
353 check(2014, 2, 1, 32);
354 check(2014, 2, 28, 59);
355 check(2014, 3, 1, 60);
356 check(2014, 4, 10, 100);
357 check(2014, 7, 19, 200);
358 check(2014, 10, 27, 300);
359 check(2014, 12, 31, 365);
360 }
361
362 #[test]
test_date_weekday()363 fn test_date_weekday() {
364 assert_eq!(NaiveDate::from_ymd_opt(1582, 10, 15).unwrap().weekday(), Weekday::Fri);
365 // May 20, 1875 = ISO 8601 reference date
366 assert_eq!(NaiveDate::from_ymd_opt(1875, 5, 20).unwrap().weekday(), Weekday::Thu);
367 assert_eq!(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().weekday(), Weekday::Sat);
368 }
369
370 #[test]
test_date_with_fields()371 fn test_date_with_fields() {
372 let d = NaiveDate::from_ymd_opt(2000, 2, 29).unwrap();
373 assert_eq!(d.with_year(-400), Some(NaiveDate::from_ymd_opt(-400, 2, 29).unwrap()));
374 assert_eq!(d.with_year(-100), None);
375 assert_eq!(d.with_year(1600), Some(NaiveDate::from_ymd_opt(1600, 2, 29).unwrap()));
376 assert_eq!(d.with_year(1900), None);
377 assert_eq!(d.with_year(2000), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap()));
378 assert_eq!(d.with_year(2001), None);
379 assert_eq!(d.with_year(2004), Some(NaiveDate::from_ymd_opt(2004, 2, 29).unwrap()));
380 assert_eq!(d.with_year(i32::MAX), None);
381
382 let d = NaiveDate::from_ymd_opt(2000, 4, 30).unwrap();
383 assert_eq!(d.with_month(0), None);
384 assert_eq!(d.with_month(1), Some(NaiveDate::from_ymd_opt(2000, 1, 30).unwrap()));
385 assert_eq!(d.with_month(2), None);
386 assert_eq!(d.with_month(3), Some(NaiveDate::from_ymd_opt(2000, 3, 30).unwrap()));
387 assert_eq!(d.with_month(4), Some(NaiveDate::from_ymd_opt(2000, 4, 30).unwrap()));
388 assert_eq!(d.with_month(12), Some(NaiveDate::from_ymd_opt(2000, 12, 30).unwrap()));
389 assert_eq!(d.with_month(13), None);
390 assert_eq!(d.with_month(u32::MAX), None);
391
392 let d = NaiveDate::from_ymd_opt(2000, 2, 8).unwrap();
393 assert_eq!(d.with_day(0), None);
394 assert_eq!(d.with_day(1), Some(NaiveDate::from_ymd_opt(2000, 2, 1).unwrap()));
395 assert_eq!(d.with_day(29), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap()));
396 assert_eq!(d.with_day(30), None);
397 assert_eq!(d.with_day(u32::MAX), None);
398 }
399
400 #[test]
test_date_with_ordinal()401 fn test_date_with_ordinal() {
402 let d = NaiveDate::from_ymd_opt(2000, 5, 5).unwrap();
403 assert_eq!(d.with_ordinal(0), None);
404 assert_eq!(d.with_ordinal(1), Some(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap()));
405 assert_eq!(d.with_ordinal(60), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap()));
406 assert_eq!(d.with_ordinal(61), Some(NaiveDate::from_ymd_opt(2000, 3, 1).unwrap()));
407 assert_eq!(d.with_ordinal(366), Some(NaiveDate::from_ymd_opt(2000, 12, 31).unwrap()));
408 assert_eq!(d.with_ordinal(367), None);
409 assert_eq!(d.with_ordinal(1 << 28 | 60), None);
410 let d = NaiveDate::from_ymd_opt(1999, 5, 5).unwrap();
411 assert_eq!(d.with_ordinal(366), None);
412 assert_eq!(d.with_ordinal(u32::MAX), None);
413 }
414
415 #[test]
test_date_num_days_from_ce()416 fn test_date_num_days_from_ce() {
417 assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1);
418
419 for year in -9999..10001 {
420 assert_eq!(
421 NaiveDate::from_ymd_opt(year, 1, 1).unwrap().num_days_from_ce(),
422 NaiveDate::from_ymd_opt(year - 1, 12, 31).unwrap().num_days_from_ce() + 1
423 );
424 }
425 }
426
427 #[test]
test_date_succ()428 fn test_date_succ() {
429 let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
430 assert_eq!(ymd(2014, 5, 6).succ_opt(), Some(ymd(2014, 5, 7)));
431 assert_eq!(ymd(2014, 5, 31).succ_opt(), Some(ymd(2014, 6, 1)));
432 assert_eq!(ymd(2014, 12, 31).succ_opt(), Some(ymd(2015, 1, 1)));
433 assert_eq!(ymd(2016, 2, 28).succ_opt(), Some(ymd(2016, 2, 29)));
434 assert_eq!(ymd(NaiveDate::MAX.year(), 12, 31).succ_opt(), None);
435 }
436
437 #[test]
test_date_pred()438 fn test_date_pred() {
439 let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
440 assert_eq!(ymd(2016, 3, 1).pred_opt(), Some(ymd(2016, 2, 29)));
441 assert_eq!(ymd(2015, 1, 1).pred_opt(), Some(ymd(2014, 12, 31)));
442 assert_eq!(ymd(2014, 6, 1).pred_opt(), Some(ymd(2014, 5, 31)));
443 assert_eq!(ymd(2014, 5, 7).pred_opt(), Some(ymd(2014, 5, 6)));
444 assert_eq!(ymd(NaiveDate::MIN.year(), 1, 1).pred_opt(), None);
445 }
446
447 #[test]
test_date_checked_add_signed()448 fn test_date_checked_add_signed() {
449 fn check(lhs: Option<NaiveDate>, delta: TimeDelta, rhs: Option<NaiveDate>) {
450 assert_eq!(lhs.unwrap().checked_add_signed(delta), rhs);
451 assert_eq!(lhs.unwrap().checked_sub_signed(-delta), rhs);
452 }
453 let ymd = NaiveDate::from_ymd_opt;
454
455 check(ymd(2014, 1, 1), TimeDelta::zero(), ymd(2014, 1, 1));
456 check(ymd(2014, 1, 1), TimeDelta::try_seconds(86399).unwrap(), ymd(2014, 1, 1));
457 // always round towards zero
458 check(ymd(2014, 1, 1), TimeDelta::try_seconds(-86399).unwrap(), ymd(2014, 1, 1));
459 check(ymd(2014, 1, 1), TimeDelta::try_days(1).unwrap(), ymd(2014, 1, 2));
460 check(ymd(2014, 1, 1), TimeDelta::try_days(-1).unwrap(), ymd(2013, 12, 31));
461 check(ymd(2014, 1, 1), TimeDelta::try_days(364).unwrap(), ymd(2014, 12, 31));
462 check(ymd(2014, 1, 1), TimeDelta::try_days(365 * 4 + 1).unwrap(), ymd(2018, 1, 1));
463 check(ymd(2014, 1, 1), TimeDelta::try_days(365 * 400 + 97).unwrap(), ymd(2414, 1, 1));
464
465 check(ymd(-7, 1, 1), TimeDelta::try_days(365 * 12 + 3).unwrap(), ymd(5, 1, 1));
466
467 // overflow check
468 check(
469 ymd(0, 1, 1),
470 TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
471 ymd(MAX_YEAR, 12, 31),
472 );
473 check(ymd(0, 1, 1), TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64 + 1).unwrap(), None);
474 check(ymd(0, 1, 1), TimeDelta::MAX, None);
475 check(
476 ymd(0, 1, 1),
477 TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(),
478 ymd(MIN_YEAR, 1, 1),
479 );
480 check(ymd(0, 1, 1), TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64 - 1).unwrap(), None);
481 check(ymd(0, 1, 1), TimeDelta::MIN, None);
482 }
483
484 #[test]
test_date_signed_duration_since()485 fn test_date_signed_duration_since() {
486 fn check(lhs: Option<NaiveDate>, rhs: Option<NaiveDate>, delta: TimeDelta) {
487 assert_eq!(lhs.unwrap().signed_duration_since(rhs.unwrap()), delta);
488 assert_eq!(rhs.unwrap().signed_duration_since(lhs.unwrap()), -delta);
489 }
490 let ymd = NaiveDate::from_ymd_opt;
491
492 check(ymd(2014, 1, 1), ymd(2014, 1, 1), TimeDelta::zero());
493 check(ymd(2014, 1, 2), ymd(2014, 1, 1), TimeDelta::try_days(1).unwrap());
494 check(ymd(2014, 12, 31), ymd(2014, 1, 1), TimeDelta::try_days(364).unwrap());
495 check(ymd(2015, 1, 3), ymd(2014, 1, 1), TimeDelta::try_days(365 + 2).unwrap());
496 check(ymd(2018, 1, 1), ymd(2014, 1, 1), TimeDelta::try_days(365 * 4 + 1).unwrap());
497 check(ymd(2414, 1, 1), ymd(2014, 1, 1), TimeDelta::try_days(365 * 400 + 97).unwrap());
498
499 check(
500 ymd(MAX_YEAR, 12, 31),
501 ymd(0, 1, 1),
502 TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
503 );
504 check(
505 ymd(MIN_YEAR, 1, 1),
506 ymd(0, 1, 1),
507 TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(),
508 );
509 }
510
511 #[test]
test_date_add_days()512 fn test_date_add_days() {
513 fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
514 assert_eq!(lhs.unwrap().checked_add_days(days), rhs);
515 }
516 let ymd = NaiveDate::from_ymd_opt;
517
518 check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
519 // always round towards zero
520 check(ymd(2014, 1, 1), Days::new(1), ymd(2014, 1, 2));
521 check(ymd(2014, 1, 1), Days::new(364), ymd(2014, 12, 31));
522 check(ymd(2014, 1, 1), Days::new(365 * 4 + 1), ymd(2018, 1, 1));
523 check(ymd(2014, 1, 1), Days::new(365 * 400 + 97), ymd(2414, 1, 1));
524
525 check(ymd(-7, 1, 1), Days::new(365 * 12 + 3), ymd(5, 1, 1));
526
527 // overflow check
528 check(ymd(0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(MAX_YEAR, 12, 31));
529 check(ymd(0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None);
530 }
531
532 #[test]
test_date_sub_days()533 fn test_date_sub_days() {
534 fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
535 assert_eq!(lhs.unwrap().checked_sub_days(days), rhs);
536 }
537 let ymd = NaiveDate::from_ymd_opt;
538
539 check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
540 check(ymd(2014, 1, 2), Days::new(1), ymd(2014, 1, 1));
541 check(ymd(2014, 12, 31), Days::new(364), ymd(2014, 1, 1));
542 check(ymd(2015, 1, 3), Days::new(365 + 2), ymd(2014, 1, 1));
543 check(ymd(2018, 1, 1), Days::new(365 * 4 + 1), ymd(2014, 1, 1));
544 check(ymd(2414, 1, 1), Days::new(365 * 400 + 97), ymd(2014, 1, 1));
545
546 check(ymd(MAX_YEAR, 12, 31), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(0, 1, 1));
547 check(
548 ymd(0, 1, 1),
549 Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap()),
550 ymd(MIN_YEAR, 1, 1),
551 );
552 }
553
554 #[test]
test_date_addassignment()555 fn test_date_addassignment() {
556 let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
557 let mut date = ymd(2016, 10, 1);
558 date += TimeDelta::try_days(10).unwrap();
559 assert_eq!(date, ymd(2016, 10, 11));
560 date += TimeDelta::try_days(30).unwrap();
561 assert_eq!(date, ymd(2016, 11, 10));
562 }
563
564 #[test]
test_date_subassignment()565 fn test_date_subassignment() {
566 let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
567 let mut date = ymd(2016, 10, 11);
568 date -= TimeDelta::try_days(10).unwrap();
569 assert_eq!(date, ymd(2016, 10, 1));
570 date -= TimeDelta::try_days(2).unwrap();
571 assert_eq!(date, ymd(2016, 9, 29));
572 }
573
574 #[test]
test_date_fmt()575 fn test_date_fmt() {
576 assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(2012, 3, 4).unwrap()), "2012-03-04");
577 assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 3, 4).unwrap()), "0000-03-04");
578 assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(-307, 3, 4).unwrap()), "-0307-03-04");
579 assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(12345, 3, 4).unwrap()), "+12345-03-04");
580
581 assert_eq!(NaiveDate::from_ymd_opt(2012, 3, 4).unwrap().to_string(), "2012-03-04");
582 assert_eq!(NaiveDate::from_ymd_opt(0, 3, 4).unwrap().to_string(), "0000-03-04");
583 assert_eq!(NaiveDate::from_ymd_opt(-307, 3, 4).unwrap().to_string(), "-0307-03-04");
584 assert_eq!(NaiveDate::from_ymd_opt(12345, 3, 4).unwrap().to_string(), "+12345-03-04");
585
586 // the format specifier should have no effect on `NaiveTime`
587 assert_eq!(format!("{:+30?}", NaiveDate::from_ymd_opt(1234, 5, 6).unwrap()), "1234-05-06");
588 assert_eq!(format!("{:30?}", NaiveDate::from_ymd_opt(12345, 6, 7).unwrap()), "+12345-06-07");
589 }
590
591 #[test]
test_date_from_str()592 fn test_date_from_str() {
593 // valid cases
594 let valid = [
595 "-0000000123456-1-2",
596 " -123456 - 1 - 2 ",
597 "-12345-1-2",
598 "-1234-12-31",
599 "-7-6-5",
600 "350-2-28",
601 "360-02-29",
602 "0360-02-29",
603 "2015-2 -18",
604 "2015-02-18",
605 "+70-2-18",
606 "+70000-2-18",
607 "+00007-2-18",
608 ];
609 for &s in &valid {
610 eprintln!("test_date_from_str valid {:?}", s);
611 let d = match s.parse::<NaiveDate>() {
612 Ok(d) => d,
613 Err(e) => panic!("parsing `{}` has failed: {}", s, e),
614 };
615 eprintln!("d {:?} (NaiveDate)", d);
616 let s_ = format!("{:?}", d);
617 eprintln!("s_ {:?}", s_);
618 // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same
619 let d_ = match s_.parse::<NaiveDate>() {
620 Ok(d) => d,
621 Err(e) => {
622 panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
623 }
624 };
625 eprintln!("d_ {:?} (NaiveDate)", d_);
626 assert!(
627 d == d_,
628 "`{}` is parsed into `{:?}`, but reparsed result \
629 `{:?}` does not match",
630 s,
631 d,
632 d_
633 );
634 }
635
636 // some invalid cases
637 // since `ParseErrorKind` is private, all we can do is to check if there was an error
638 let invalid = [
639 "", // empty
640 "x", // invalid
641 "Fri, 09 Aug 2013 GMT", // valid date, wrong format
642 "Sat Jun 30 2012", // valid date, wrong format
643 "1441497364.649", // valid datetime, wrong format
644 "+1441497364.649", // valid datetime, wrong format
645 "+1441497364", // valid datetime, wrong format
646 "2014/02/03", // valid date, wrong format
647 "2014", // datetime missing data
648 "2014-01", // datetime missing data
649 "2014-01-00", // invalid day
650 "2014-11-32", // invalid day
651 "2014-13-01", // invalid month
652 "2014-13-57", // invalid month, day
653 "9999999-9-9", // invalid year (out of bounds)
654 ];
655 for &s in &invalid {
656 eprintln!("test_date_from_str invalid {:?}", s);
657 assert!(s.parse::<NaiveDate>().is_err());
658 }
659 }
660
661 #[test]
test_date_parse_from_str()662 fn test_date_parse_from_str() {
663 let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
664 assert_eq!(
665 NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
666 Ok(ymd(2014, 5, 7))
667 ); // ignore time and offset
668 assert_eq!(
669 NaiveDate::parse_from_str("2015-W06-1=2015-033", "%G-W%V-%u = %Y-%j"),
670 Ok(ymd(2015, 2, 2))
671 );
672 assert_eq!(NaiveDate::parse_from_str("Fri, 09 Aug 13", "%a, %d %b %y"), Ok(ymd(2013, 8, 9)));
673 assert!(NaiveDate::parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err());
674 assert!(NaiveDate::parse_from_str("2014-57", "%Y-%m-%d").is_err());
675 assert!(NaiveDate::parse_from_str("2014", "%Y").is_err()); // insufficient
676
677 assert_eq!(
678 NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
679 NaiveDate::from_ymd_opt(2020, 1, 12),
680 );
681
682 assert_eq!(
683 NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(),
684 NaiveDate::from_ymd_opt(2019, 1, 13),
685 );
686 }
687
688 #[test]
test_day_iterator_limit()689 fn test_day_iterator_limit() {
690 assert_eq!(NaiveDate::from_ymd_opt(MAX_YEAR, 12, 29).unwrap().iter_days().take(4).count(), 2);
691 assert_eq!(
692 NaiveDate::from_ymd_opt(MIN_YEAR, 1, 3).unwrap().iter_days().rev().take(4).count(),
693 2
694 );
695 }
696
697 #[test]
test_week_iterator_limit()698 fn test_week_iterator_limit() {
699 assert_eq!(NaiveDate::from_ymd_opt(MAX_YEAR, 12, 12).unwrap().iter_weeks().take(4).count(), 2);
700 assert_eq!(
701 NaiveDate::from_ymd_opt(MIN_YEAR, 1, 15).unwrap().iter_weeks().rev().take(4).count(),
702 2
703 );
704 }
705
706 #[test]
test_weeks_from()707 fn test_weeks_from() {
708 // tests per: https://github.com/chronotope/chrono/issues/961
709 // these internally use `weeks_from` via the parsing infrastructure
710 assert_eq!(
711 NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
712 NaiveDate::from_ymd_opt(2020, 1, 12),
713 );
714 assert_eq!(
715 NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(),
716 NaiveDate::from_ymd_opt(2019, 1, 13),
717 );
718
719 // direct tests
720 for (y, starts_on) in &[
721 (2019, Weekday::Tue),
722 (2020, Weekday::Wed),
723 (2021, Weekday::Fri),
724 (2022, Weekday::Sat),
725 (2023, Weekday::Sun),
726 (2024, Weekday::Mon),
727 (2025, Weekday::Wed),
728 (2026, Weekday::Thu),
729 ] {
730 for day in &[
731 Weekday::Mon,
732 Weekday::Tue,
733 Weekday::Wed,
734 Weekday::Thu,
735 Weekday::Fri,
736 Weekday::Sat,
737 Weekday::Sun,
738 ] {
739 assert_eq!(
740 NaiveDate::from_ymd_opt(*y, 1, 1).map(|d| d.weeks_from(*day)),
741 Some(if day == starts_on { 1 } else { 0 })
742 );
743
744 // last day must always be in week 52 or 53
745 assert!(
746 [52, 53].contains(&NaiveDate::from_ymd_opt(*y, 12, 31).unwrap().weeks_from(*day)),
747 );
748 }
749 }
750
751 let base = NaiveDate::from_ymd_opt(2019, 1, 1).unwrap();
752
753 // 400 years covers all year types
754 for day in &[
755 Weekday::Mon,
756 Weekday::Tue,
757 Weekday::Wed,
758 Weekday::Thu,
759 Weekday::Fri,
760 Weekday::Sat,
761 Weekday::Sun,
762 ] {
763 // must always be below 54
764 for dplus in 1..(400 * 366) {
765 assert!((base + Days::new(dplus)).weeks_from(*day) < 54)
766 }
767 }
768 }
769
770 #[test]
test_with_0_overflow()771 fn test_with_0_overflow() {
772 let dt = NaiveDate::from_ymd_opt(2023, 4, 18).unwrap();
773 assert!(dt.with_month0(4294967295).is_none());
774 assert!(dt.with_day0(4294967295).is_none());
775 assert!(dt.with_ordinal0(4294967295).is_none());
776 }
777
778 #[test]
test_leap_year()779 fn test_leap_year() {
780 for year in 0..=MAX_YEAR {
781 let date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap();
782 let is_leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
783 assert_eq!(date.leap_year(), is_leap);
784 assert_eq!(date.leap_year(), date.with_ordinal(366).is_some());
785 }
786 }
787
788 #[test]
test_date_yearflags()789 fn test_date_yearflags() {
790 for (year, year_flags, _) in YEAR_FLAGS {
791 assert_eq!(NaiveDate::from_yo_opt(year, 1).unwrap().year_flags(), year_flags);
792 }
793 }
794
795 #[test]
test_weekday_with_yearflags()796 fn test_weekday_with_yearflags() {
797 for (year, year_flags, first_weekday) in YEAR_FLAGS {
798 let first_day_of_year = NaiveDate::from_yo_opt(year, 1).unwrap();
799 dbg!(year);
800 assert_eq!(first_day_of_year.year_flags(), year_flags);
801 assert_eq!(first_day_of_year.weekday(), first_weekday);
802
803 let mut prev = first_day_of_year.weekday();
804 for ordinal in 2u32..=year_flags.ndays() {
805 let date = NaiveDate::from_yo_opt(year, ordinal).unwrap();
806 let expected = prev.succ();
807 assert_eq!(date.weekday(), expected);
808 prev = expected;
809 }
810 }
811 }
812
813 #[test]
test_isoweekdate_with_yearflags()814 fn test_isoweekdate_with_yearflags() {
815 for (year, year_flags, _) in YEAR_FLAGS {
816 // January 4 should be in the first week
817 let jan4 = NaiveDate::from_ymd_opt(year, 1, 4).unwrap();
818 let iso_week = jan4.iso_week();
819 assert_eq!(jan4.year_flags(), year_flags);
820 assert_eq!(iso_week.week(), 1);
821 }
822 }
823
824 #[test]
test_date_to_mdf_to_date()825 fn test_date_to_mdf_to_date() {
826 for (year, year_flags, _) in YEAR_FLAGS {
827 for ordinal in 1..=year_flags.ndays() {
828 let date = NaiveDate::from_yo_opt(year, ordinal).unwrap();
829 assert_eq!(date, NaiveDate::from_mdf(date.year(), date.mdf()).unwrap());
830 }
831 }
832 }
833
834 // Used for testing some methods with all combinations of `YearFlags`.
835 // (year, flags, first weekday of year)
836 const YEAR_FLAGS: [(i32, YearFlags, Weekday); 14] = [
837 (2006, A, Weekday::Sun),
838 (2005, B, Weekday::Sat),
839 (2010, C, Weekday::Fri),
840 (2009, D, Weekday::Thu),
841 (2003, E, Weekday::Wed),
842 (2002, F, Weekday::Tue),
843 (2001, G, Weekday::Mon),
844 (2012, AG, Weekday::Sun),
845 (2000, BA, Weekday::Sat),
846 (2016, CB, Weekday::Fri),
847 (2004, DC, Weekday::Thu),
848 (2020, ED, Weekday::Wed),
849 (2008, FE, Weekday::Tue),
850 (2024, GF, Weekday::Mon),
851 ];
852
853 #[test]
854 #[cfg(feature = "rkyv-validation")]
test_rkyv_validation()855 fn test_rkyv_validation() {
856 let date_min = NaiveDate::MIN;
857 let bytes = rkyv::to_bytes::<_, 4>(&date_min).unwrap();
858 assert_eq!(rkyv::from_bytes::<NaiveDate>(&bytes).unwrap(), date_min);
859
860 let date_max = NaiveDate::MAX;
861 let bytes = rkyv::to_bytes::<_, 4>(&date_max).unwrap();
862 assert_eq!(rkyv::from_bytes::<NaiveDate>(&bytes).unwrap(), date_max);
863 }
864
865 // MAX_YEAR-12-31 minus 0000-01-01
866 // = (MAX_YEAR-12-31 minus 0000-12-31) + (0000-12-31 - 0000-01-01)
867 // = MAX_YEAR * 365 + (# of leap years from 0001 to MAX_YEAR) + 365
868 // = (MAX_YEAR + 1) * 365 + (# of leap years from 0001 to MAX_YEAR)
869 const MAX_DAYS_FROM_YEAR_0: i32 =
870 (MAX_YEAR + 1) * 365 + MAX_YEAR / 4 - MAX_YEAR / 100 + MAX_YEAR / 400;
871
872 // MIN_YEAR-01-01 minus 0000-01-01
873 // = MIN_YEAR * 365 + (# of leap years from MIN_YEAR to 0000)
874 const MIN_DAYS_FROM_YEAR_0: i32 = MIN_YEAR * 365 + MIN_YEAR / 4 - MIN_YEAR / 100 + MIN_YEAR / 400;
875
876 // only used for testing, but duplicated in naive::datetime
877 const MAX_BITS: usize = 44;
878