gregorian 0.2.4

easy to use date library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
use crate::{DateParseError, InvalidDate, InvalidDayOfMonth, InvalidDateSyntax, Month, Year, YearMonth};
use crate::util::{modulo_i16, modulo_i32};

/// The total number of days in 400 years.
const DAYS_IN_400_YEAR: i32 = 400 * 365 + 97;

/// The number of days since year 0 for 1970-01-01.
const UNIX_EPOCH: i32 = DAYS_IN_400_YEAR * 4 + 370 * 365 + 90;

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// A calendar date consting of a year, month and day.
///
/// All dates in the library use the proleptic Gregorian calendar with a year 0.
pub struct Date {
	pub(crate) year: Year,
	pub(crate) month: Month,
	pub(crate) day: u8,
}

#[cfg(feature = "serde")]
impl serde::Serialize for Date {
	fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
		serializer.collect_str(self)
	}
}

#[cfg(feature = "serde")]
impl<'a> serde::Deserialize<'a> for Date {
	fn deserialize<D: serde::Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
		let raw = std::borrow::Cow::<'_, str>::deserialize(deserializer)?;
		raw.parse().map_err(serde::de::Error::custom)
	}
}

impl Date {
	/// Create a new date from a year, month and day.
	///
	/// Month and day numbers start at 1.
	pub fn new<Y, M>(year: Y, month: M, day: u8) -> Result<Self, InvalidDate>
	where
		Y: Into<Year>,
		M: core::convert::TryInto<Month>,
		InvalidDate: From<M::Error>,
	{
		let year_month = YearMonth::new(year, month.try_into()?);
		Ok(year_month.with_day(day)?)
	}

	/// Create a new date from a year, month and day.
	///
	/// Day numbers start at 1.
	pub const fn new_const(year: Year, month: Month, day: u8) -> Result<Self, InvalidDayOfMonth> {
		YearMonth::new_const(year, month).with_day(day)
	}

	/// Create a new date without checking the validity.
	///
	/// Month and day numbers start at 1.
	///
	/// # Safety
	/// Although this is currently not the case,
	/// future implementations may rely on date validity for memory safety
	pub const unsafe fn new_unchecked(year: Year, month: Month, day: u8) -> Self {
		Self { year, month, day }
	}

	/// Get the current date in the local time zone.
	#[cfg(feature = "std")]
	pub fn today() -> Self {
		unsafe {
			let time = libc::time(std::ptr::null_mut());
			let mut tm: libc::tm = std::mem::zeroed();
			#[cfg(not(target_os = "windows"))]
			if libc::localtime_r(&time, &mut tm).is_null() {
				panic!("failed to determine current time in local time zone: {}", std::io::Error::last_os_error());
			}
			#[cfg(target_os = "windows")]
			{
				let error = libc::localtime_s(&mut tm, &time);
				if error != 0 {
					panic!("failed to determine current time in local time zone: error {}", error);
				}
			}
			let year = Year::new(tm.tm_year as i16 + 1900);
			let month = Month::new_unchecked(tm.tm_mon as u8 + 1);
			let day = tm.tm_mday as u8; // Weirdly, tm_mday is 1 based while tm_mon is zero based.
			Date::new_unchecked(year, month, day)
		}
	}

	/// Get the current date of the UTC time zone.
	#[cfg(feature = "std")]
	pub fn today_utc() -> Self {
		let seconds = std::time::SystemTime::now()
			.duration_since(std::time::SystemTime::UNIX_EPOCH)
			.unwrap()
			.as_secs();
		let days = seconds / 60 / 60 / 24;
		Self::from_days_since_year_zero(UNIX_EPOCH + days as i32)
	}

	/// Get the date for a unix timestamp.
	///
	/// The timestamp is interpreted as number of seconds since 1 January 1970 00:00,
	/// not including any leap seconds.
	pub const fn from_unix_timestamp(seconds: i64) -> Self {
		let days = seconds / (24 * 3600);
		let days = if seconds < 0 && seconds != days * 24 * 3600 {
			days - 1
		} else {
			days
		};

		Self::from_days_since_year_zero(UNIX_EPOCH + days as i32)
	}

	/// Get the unix timestamp for a date.
	///
	/// The timestamp is the number of seconds since 1 January 1970 00:00.
	///
	/// The returned timestamp is valid for time 00:00 of the date.
	pub const fn to_unix_timestamp(self) -> i64 {
		let days = self.days_since_year_zero() - UNIX_EPOCH;
		60 * 60 * 24 * days as i64
	}

	/// Get the year.
	pub const fn year(self) -> Year {
		self.year
	}

	/// Get the month.
	pub const fn month(self) -> Month {
		self.month
	}

	/// Get the day of the month.
	pub const fn day(self) -> u8 {
		self.day
	}

	/// Get the year and month as [`YearMonth`].
	pub const fn year_month(self) -> YearMonth {
		YearMonth::new_const(self.year(), self.month())
	}

	/// Get the day of the year.
	///
	/// The returned number is 1-based.
	/// For January 1, this function will return 1.
	pub const fn day_of_year(self) -> u16 {
		crate::raw::day_of_year(self.month, self.day, self.year.has_leap_day())
	}

	/// The number of days remaining in the year, including the current date.
	///
	/// For Janury 1 this will return 365 in a non-leap year or 366 in a leap year.
	/// For December 31, this will return 1.
	pub const fn days_remaining_in_year(self) -> u16 {
		self.year.total_days() - self.day_of_year() + 1
	}

	/// Get the total number of days since 1 January 0000.
	///
	/// The returned value is zero-based.
	/// For 1 January 0000, this function returns 0.
	#[allow(clippy::identity_op)]
	pub const fn days_since_year_zero(self) -> i32 {
		let years = modulo_i16(self.year().to_number(), 400);
		let whole_cycles = (self.year().to_number() - years) / 400;

		// Plus one because year 0 is a leap year.
		let leap_days = years / 4 - years / 100 + 1;
		// But -1 in leap years because they're taken care of in self.day_of_year().
		let leap_days = leap_days - if self.year.has_leap_day() { 1 } else { 0 };

		let from_years = whole_cycles as i32 * DAYS_IN_400_YEAR + years as i32 * 365 + leap_days as i32;

		from_years + self.day_of_year() as i32 - 1
	}

	/// Get the date corresponding to a number of days since the year zero.
	///
	/// For this function, day 0 is 1 January of year 0.
	#[rustfmt::skip]
	pub const fn from_days_since_year_zero(days: i32) -> Self {
		// Get the day index in the current 400 year cycle,
		// and the number of passed 400 year cycles.
		let day_index = modulo_i32(days, DAYS_IN_400_YEAR);
		let whole_cycles = (days - day_index) / DAYS_IN_400_YEAR;

		// How many leaps days did not happen at year 100, 200 and 300?
		let pretend_leap_days;
		if day_index >= 300 * 365 + 73 + 31 + 28 {
			pretend_leap_days = 3;
		} else if day_index >= 200 * 365 + 49 + 31 + 28 {
			pretend_leap_days = 2;
		} else if day_index >= 100 * 365 + 25 + 31 + 28 {
			pretend_leap_days = 1;
		} else {
			pretend_leap_days = 0;
		}

		// How many four year intervals passed, and how many days since then?
		let four_year_cycles       = (day_index + pretend_leap_days) / (4 * 365 + 1);
		let day_of_four_year_cycle = (day_index + pretend_leap_days) % (4 * 365 + 1);

		// How many years passed since the 4 year interval?
		let year_of_four_year_cycle = (day_of_four_year_cycle - 1) / 365;

		// Calculate the day of the year.
		// We added pretendsies leap days for year 100, 200 and 300,
		// so we can ignore the fact that those years actually don't have one.
		let day_of_year = day_of_four_year_cycle - (year_of_four_year_cycle * 365);
		let day_of_year = day_of_year - if day_of_four_year_cycle >= 366 { 1 } else { 0 };

		// Compensate for 1 based year-of-day numbers.
		let day_of_year = day_of_year + 1;

		// Put it all together.
		let year = 400 * whole_cycles + 4 * four_year_cycles + year_of_four_year_cycle;
		let year = Year::new(year as i16);

		// Lie about leap years for year 100, 200 and 300 because we added pretend leaps days.
		let (month, day_of_month) = match crate::raw::month_and_day_from_day_of_year(day_of_year as u16, year_of_four_year_cycle == 0) {
			Ok(x) => x,
			// TODO: replace with unreachable! when const_panic is stabilized.
			Err(()) => (Month::January, 1),
		};

		unsafe { year.with_month(month).with_day_unchecked(day_of_month) }
	}

	/// Get a [`Date`] object for the next day.
	pub const fn next(self) -> Date {
		if self.day == self.year_month().total_days() {
			self.year_month().next().first_day()
		} else {
			Self {
				year: self.year,
				month: self.month,
				day: self.day + 1,
			}
		}
	}

	/// Get a [`Date`] object for the previous day.
	pub const fn prev(self) -> Date {
		if self.day == 1 {
			self.year_month().prev().last_day()
		} else {
			Self {
				year: self.year,
				month: self.month,
				day: self.day - 1,
			}
		}
	}

	/// Compute a date by adding days.
	pub const fn add_days(self, days: i32) -> Self {
		Self::from_days_since_year_zero(self.days_since_year_zero() + days)
	}

	/// Compute a date by subtracting days.
	pub const fn sub_days(self, days: i32) -> Self {
		Self::from_days_since_year_zero(self.days_since_year_zero() - days)
	}

	/// Compute a date by adding a number of months.
	///
	/// The resulting date may not be valid.
	/// You can call [`InvalidDayOfMonth::next_valid()`] or [`InvalidDayOfMonth::prev_valid()`]
	/// to get the first day of the next month or the last day of resulting month.
	pub const fn add_months(self, months: i32) -> Result<Self, InvalidDayOfMonth> {
		self.year_month().add_months(months).with_day(self.day())
	}

	/// Compute a date by subtracting a number of months.
	///
	/// The resulting date may not be valid.
	/// You can call [`InvalidDayOfMonth::next_valid()`] or [`InvalidDayOfMonth::prev_valid()`]
	/// to get the first day of the next month or the last day of resulting month.
	pub const fn sub_months(self, months: i32) -> Result<Self, InvalidDayOfMonth> {
		self.year_month().add_months(months).with_day(self.day())
	}

	/// Compute a date by adding a number of years.
	///
	/// The resulting date may not be valid.
	/// You can call [`InvalidDayOfMonth::next_valid()`] or [`InvalidDayOfMonth::prev_valid()`]
	/// to get the first day of the next month or the last day of resulting month.
	pub const fn add_years(self, years: i16) -> Result<Self, InvalidDayOfMonth> {
		self.year_month().add_years(years).with_day(self.day())
	}

	/// Compute a date by subtracting a number of years.
	///
	/// The resulting date may not be valid.
	/// You can call [`InvalidDayOfMonth::next_valid()`] or [`InvalidDayOfMonth::prev_valid()`]
	/// to get the first day of the next month or the last day of resulting month.
	pub const fn sub_years(self, years: i16) -> Result<Self, InvalidDayOfMonth> {
		self.year_month().add_years(years).with_day(self.day())
	}

	/// Compute the difference in days between two dates.
	///
	/// This does not include the end date.
	/// For example, difference between the same date will be 0.
	pub const fn days_since(self, other: Date) -> i32 {
		other.days_since_year_zero() - self.days_since_year_zero()
	}
}

impl core::str::FromStr for Date {
	type Err = DateParseError;

	fn from_str(data: &str) -> Result<Self, Self::Err> {
		// Extract fields.
		let mut fields = data.splitn(3, '-');
		let year = fields.next().unwrap();
		let month = fields.next().ok_or_else(InvalidDateSyntax::new)?;
		let day = fields.next().ok_or_else(InvalidDateSyntax::new)?;

		// Parse fields as numbers.
		let year: i16 = year.parse().map_err(|_| InvalidDateSyntax::new())?;
		let month: u8 = month.parse().map_err(|_| InvalidDateSyntax::new())?;
		let day: u8 = day.parse().map_err(|_| InvalidDateSyntax::new())?;

		// Return date.
		Ok(Self::new(year, month, day)?)
	}
}

impl core::fmt::Display for Date {
	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
		write!(f, "{:04}-{:02}-{:02}", self.year.to_number(), self.month.to_number(), self.day)
	}
}

impl core::fmt::Debug for Date {
	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
		write!(f, "Date({})", self)
	}
}

#[cfg(test)]
mod test {
	use super::*;
	use assert2::{assert, let_assert};

	#[test]
	fn new() {
		assert!(let Ok(_) = Date::new(2020, 1, 2));
		assert!(Date::new(2020, 1, 2).unwrap().year() == 2020);
		assert!(Date::new(2020, 1, 2).unwrap().month() == 1);
		assert!(Date::new(2020, 1, 2).unwrap().day() == 2);

		assert!(let Ok(_) = Date::new(2020, 2, 29));
		assert!(let Err(_) = Date::new(2020, 2, 30));
		assert!(let Ok(_) = Date::new(2019, 2, 28));
		assert!(let Err(_) = Date::new(2019, 2, 29));
	}

	#[test]
	#[cfg(feature = "std")]
	fn today() {
		// Got nothing to check it against really, but lets see at least that it does not panic,
		// and that it's atleast 2021.
		assert!(Date::today().year() >= 2021);
		assert!(Date::today_utc().year() >= 2021);
	}

	#[test]
	fn next() {
		assert!(Date::new(2020, 1, 2).unwrap().next() == Date::new(2020, 1, 3).unwrap());
		assert!(Date::new(2020, 1, 31).unwrap().next() == Date::new(2020, 2, 1).unwrap());
		assert!(Date::new(2020, 12, 31).unwrap().next() == Date::new(2021, 1, 1).unwrap());
	}

	#[test]
	fn day_of_year() {
		assert!(Date::new(2019, 1, 1).unwrap().day_of_year() == 1);
		assert!(Date::new(2019, 2, 1).unwrap().day_of_year() == 32);
		assert!(Date::new(2019, 3, 1).unwrap().day_of_year() == 60);
		assert!(Date::new(2019, 4, 1).unwrap().day_of_year() == 91);
		assert!(Date::new(2019, 5, 1).unwrap().day_of_year() == 121);
		assert!(Date::new(2019, 6, 1).unwrap().day_of_year() == 152);
		assert!(Date::new(2019, 7, 1).unwrap().day_of_year() == 182);
		assert!(Date::new(2019, 8, 1).unwrap().day_of_year() == 213);
		assert!(Date::new(2019, 9, 1).unwrap().day_of_year() == 244);
		assert!(Date::new(2019, 10, 1).unwrap().day_of_year() == 274);
		assert!(Date::new(2019, 11, 1).unwrap().day_of_year() == 305);
		assert!(Date::new(2019, 12, 1).unwrap().day_of_year() == 335);

		assert!(Date::new(2020, 1, 1).unwrap().day_of_year() == 1);
		assert!(Date::new(2020, 2, 1).unwrap().day_of_year() == 32);
		assert!(Date::new(2020, 3, 1).unwrap().day_of_year() == 61);
		assert!(Date::new(2020, 4, 1).unwrap().day_of_year() == 92);
		assert!(Date::new(2020, 5, 1).unwrap().day_of_year() == 122);
		assert!(Date::new(2020, 6, 1).unwrap().day_of_year() == 153);
		assert!(Date::new(2020, 7, 1).unwrap().day_of_year() == 183);
		assert!(Date::new(2020, 8, 1).unwrap().day_of_year() == 214);
		assert!(Date::new(2020, 9, 1).unwrap().day_of_year() == 245);
		assert!(Date::new(2020, 10, 1).unwrap().day_of_year() == 275);
		assert!(Date::new(2020, 11, 1).unwrap().day_of_year() == 306);
		assert!(Date::new(2020, 12, 1).unwrap().day_of_year() == 336);

		assert!(Date::new(2019, 1, 2).unwrap().day_of_year() == 2);
		assert!(Date::new(2019, 1, 31).unwrap().day_of_year() == 31);
		assert!(Date::new(2019, 2, 2).unwrap().day_of_year() == 33);
		assert!(Date::new(2019, 2, 28).unwrap().day_of_year() == 59);
		assert!(Date::new(2019, 12, 31).unwrap().day_of_year() == 365);

		assert!(Date::new(2020, 12, 31).unwrap().day_of_year() == 366);

		let mut date = Date::new(2020, 1, 1).unwrap();
		for i in 1..=366 {
			assert!(date.day_of_year() == i);
			date = date.next();
		}

		let mut date = Date::new(2021, 1, 1).unwrap();
		for i in 1..=365 {
			assert!(date.day_of_year() == i);
			date = date.next();
		}
	}

	#[test]
	#[allow(clippy::identity_op, clippy::neg_multiply)]
	fn days_since_year_zero() {
		assert!(Date::new(0, 1, 1).unwrap().days_since_year_zero() == 0);
		assert!(Date::new(400, 1, 1).unwrap().days_since_year_zero() == 1 * (400 * 365 + 97));
		assert!(Date::new(800, 1, 1).unwrap().days_since_year_zero() == 2 * (400 * 365 + 97));
		assert!(Date::new(-400, 1, 1).unwrap().days_since_year_zero() == -1 * (400 * 365 + 97));
		assert!(Date::new(-800, 1, 1).unwrap().days_since_year_zero() == -2 * (400 * 365 + 97));

		assert!(Date::new(1, 1, 1).unwrap().days_since_year_zero() == 366);
		assert!(Date::new(0, 12, 31).unwrap().days_since_year_zero() == 365);
		assert!(Date::new(399, 12, 31).unwrap().days_since_year_zero() == 400 * 365 + 97 - 1);
		assert!(Date::new(-1, 12, 31).unwrap().days_since_year_zero() == -1);

		assert!(Date::new(396, 1, 1).unwrap().days_since_year_zero() == 396 * 365 + 96);

		assert!(Date::new(-2, 1, 1).unwrap().days_since_year_zero() == -2 * 365);
		assert!(Date::new(-3, 1, 1).unwrap().days_since_year_zero() == -3 * 365);
		assert!(Date::new(-4, 1, 1).unwrap().days_since_year_zero() == -4 * 365 - 1);
		assert!(Date::new(-100, 1, 1).unwrap().days_since_year_zero() == -100 * 365 - 24);
		assert!(Date::new(-400, 1, 1).unwrap().days_since_year_zero() == -400 * 365 - 97);

		let mut date = Date::new(0, 1, 1).unwrap();
		for i in 0..=10 * DAYS_IN_400_YEAR {
			assert!(date.days_since_year_zero() == i);
			date = date.next();
		}

		let mut date = Date::new(0, 1, 1).unwrap();
		for i in (-10 * DAYS_IN_400_YEAR..=0).rev() {
			assert!(date.days_since_year_zero() == i);
			date = date.prev();
		}
	}

	#[test]
	#[allow(clippy::identity_op, clippy::neg_multiply)]
	fn from_days_since_year_zero() {
		assert!(Date::from_days_since_year_zero(0) == Date::new(0, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(1 * (400 * 365 + 97)) == Date::new(400, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(2 * (400 * 365 + 97)) == Date::new(800, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(-1 * (400 * 365 + 97)) == Date::new(-400, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(-2 * (400 * 365 + 97)) == Date::new(-800, 1, 1).unwrap());

		assert!(Date::from_days_since_year_zero(366) == Date::new(1, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(365) == Date::new(0, 12, 31).unwrap());
		assert!(Date::from_days_since_year_zero(400 * 365 + 97 - 1) == Date::new(399, 12, 31).unwrap());
		assert!(Date::from_days_since_year_zero(-1) == Date::new(-1, 12, 31).unwrap());

		assert!(Date::from_days_since_year_zero(396 * 365 + 96) == Date::new(396, 1, 1).unwrap());

		assert!(Date::from_days_since_year_zero(-2 * 365) == Date::new(-2, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(-3 * 365) == Date::new(-3, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(-4 * 365 - 1) == Date::new(-4, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(366) == Date::new(1, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(100 * 365 + 25 + 31 + 27) == Date::new(100, 2, 28).unwrap());
		assert!(Date::from_days_since_year_zero(100 * 365 + 25 + 31 + 28) == Date::new(100, 3, 1).unwrap());
		assert!(Date::from_days_since_year_zero(100 * 365 + 25 + 31 + 29) == Date::new(100, 3, 2).unwrap());
		assert!(Date::from_days_since_year_zero(100 * 365 + 25 + 31 + 30) == Date::new(100, 3, 3).unwrap());
		assert!(Date::from_days_since_year_zero(101 * 365 + 25) == Date::new(101, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(100 * 365 + 25 + 31 + 28) == Date::new(100, 3, 1).unwrap());
		assert!(Date::from_days_since_year_zero(200 * 365 + 49) == Date::new(200, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(300 * 365 + 73) == Date::new(300, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(-100 * 365 - 24) == Date::new(-100, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(-400 * 365 - 97) == Date::new(-400, 1, 1).unwrap());

		assert!(Date::from_days_since_year_zero(370 * 365 + 90) == Date::new(370, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(770 * 365 + 97 + 90) == Date::new(770, 1, 1).unwrap());
		assert!(Date::from_days_since_year_zero(UNIX_EPOCH) == Date::new(1970, 1, 1).unwrap());

		let mut date = Date::new(0, 1, 1).unwrap();
		for i in 0..=10 * DAYS_IN_400_YEAR {
			assert!(Date::from_days_since_year_zero(i) == date);
			date = date.next();
		}

		let mut date = Date::new(0, 1, 1).unwrap();
		for i in (-10 * DAYS_IN_400_YEAR..=0).rev() {
			assert!(Date::from_days_since_year_zero(i) == date);
			date = date.prev();
		}
	}

	#[test]
	fn add_days() {
		assert!(Date::new(2020, 1, 1).unwrap().add_days(1) == Date::new(2020, 1, 2).unwrap());
		assert!(Date::new(2020, 1, 1).unwrap().add_days(31) == Date::new(2020, 2, 1).unwrap());
		assert!(Date::new(2020, 1, 1).unwrap().add_days(366) == Date::new(2021, 1, 1).unwrap());
		assert!(Date::new(2020, 1, 1).unwrap().add_days(366 + 365) == Date::new(2022, 1, 1).unwrap());

		assert!(Date::new(2000, 1, 1).unwrap().add_days(100 * 365 + 24) == Date::new(2099, 12, 31).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(100 * 365 + 25) == Date::new(2100, 1, 1).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(100 * 365 + 26) == Date::new(2100, 1, 2).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(100 * 365 + 25 + 58) == Date::new(2100, 2, 28).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(100 * 365 + 25 + 59) == Date::new(2100, 3, 1).unwrap());

		assert!(Date::new(2000, 1, 1).unwrap().add_days(200 * 365 + 48) == Date::new(2199, 12, 31).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(200 * 365 + 49) == Date::new(2200, 1, 1).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(200 * 365 + 50) == Date::new(2200, 1, 2).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(200 * 365 + 49 + 58) == Date::new(2200, 2, 28).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(200 * 365 + 49 + 59) == Date::new(2200, 3, 1).unwrap());

		assert!(Date::new(2000, 1, 1).unwrap().add_days(300 * 365 + 73) == Date::new(2300, 1, 1).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(300 * 365 + 72) == Date::new(2299, 12, 31).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(300 * 365 + 74) == Date::new(2300, 1, 2).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(300 * 365 + 73 + 58) == Date::new(2300, 2, 28).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(300 * 365 + 73 + 59) == Date::new(2300, 3, 1).unwrap());

		assert!(Date::new(2000, 1, 1).unwrap().add_days(400 * 365 + 96) == Date::new(2399, 12, 31).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(400 * 365 + 97) == Date::new(2400, 1, 1).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(400 * 365 + 98) == Date::new(2400, 1, 2).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(400 * 365 + 97 + 58) == Date::new(2400, 2, 28).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(400 * 365 + 97 + 59) == Date::new(2400, 2, 29).unwrap());
		assert!(Date::new(2000, 1, 1).unwrap().add_days(400 * 365 + 97 + 60) == Date::new(2400, 3, 1).unwrap());
	}

	#[test]
	fn add_years() {
		assert!(Date::new(2020, 1, 1).unwrap().add_years(1).unwrap() == Date::new(2021, 1, 1).unwrap());
		assert!(Date::new(2000, 2, 29).unwrap().add_years(400).unwrap() == Date::new(2400, 2, 29).unwrap());
		assert!(Date::new(2000, 2, 29).unwrap().add_years(100).unwrap_err().prev_valid() == Date::new(2100, 2, 28).unwrap());
		assert!(Date::new(2000, 2, 29).unwrap().add_years(100).unwrap_err().next_valid() == Date::new(2100, 3, 1).unwrap());
	}

	#[test]
	fn add_months() {
		assert!(Date::new(2021, 1, 31).unwrap().add_months(2).unwrap() == Date::new(2021, 3, 31).unwrap());
		assert!(Date::new(2021, 1, 31).unwrap().add_months(1).unwrap_err().prev_valid() == Date::new(2021, 2, 28).unwrap());
		assert!(Date::new(2021, 1, 31).unwrap().add_months(1).unwrap_err().next_valid() == Date::new(2021, 3, 1).unwrap());

		assert!(Date::new(2021, 1, 31).unwrap().add_months(14).unwrap() == Date::new(2022, 3, 31).unwrap());
		assert!(Date::new(2021, 1, 31).unwrap().add_months(13).unwrap_err().prev_valid() == Date::new(2022, 2, 28).unwrap());
		assert!(Date::new(2021, 1, 31).unwrap().add_months(13).unwrap_err().next_valid() == Date::new(2022, 3, 1).unwrap());
	}

	#[test]
	fn parse() {
		assert!("2020-01-02".parse::<Date>().unwrap().year() == 2020);
		assert!("2020-01-02".parse::<Date>().unwrap().month() == 1);
		assert!("2020-01-02".parse::<Date>().unwrap().day() == 2);
		assert!(let Err(DateParseError::InvalidDateSyntax(_)) = "not-a-date".parse::<Date>());
		assert!(let Err(DateParseError::InvalidDate(_)) = "2019-30-12".parse::<Date>());
	}

	#[test]
	fn from_unix_timestamp() {
		const SECONDS_IN_DAY: i64 = 60 * 60 * 24;
		assert!(Date::from_unix_timestamp(0) == Date::new(1970, 1, 1).unwrap());
		assert!(Date::from_unix_timestamp(SECONDS_IN_DAY) == Date::new(1970, 1, 2).unwrap());
		assert!(Date::from_unix_timestamp(1592611200) == Date::new(2020, 6, 20).unwrap());
		assert!(Date::from_unix_timestamp(1592697599) == Date::new(2020, 6, 20).unwrap());
		assert!(Date::from_unix_timestamp(1592697600) == Date::new(2020, 6, 21).unwrap());
	}

	#[test]
	fn to_unix_timestamp() {
		const SECONDS_IN_DAY: i64 = 60 * 60 * 24;
		assert!(Date::new(1970, 1, 1).unwrap().to_unix_timestamp() == 0);
		assert!(Date::new(1970, 1, 2).unwrap().to_unix_timestamp() == SECONDS_IN_DAY);
		assert!(Date::new(2020, 6, 20).unwrap().to_unix_timestamp() == 1592611200);
	}

	#[test]
	fn days_since() {
		assert!(Date::new(1970, 1, 1).unwrap().days_since(Date::new(1970, 1, 1).unwrap()) == 0);
		assert!(Date::new(1970, 1, 1).unwrap().days_since(Date::new(1970, 1, 31).unwrap()) == 30);
		assert!(Date::new(1970, 1, 1).unwrap().days_since(Date::new(2022, 4, 8).unwrap()) == 19090);
	}

	#[test]
	#[cfg(feature = "std")]
	fn format() {
		assert!(format!("{}", Date::new(2020, Month::January, 2).unwrap()) == "2020-01-02");
		assert!(format!("{:?}", Date::new(2020, Month::January, 2).unwrap()) == "Date(2020-01-02)");
	}

	#[test]
	fn serde() {
		#[derive(Debug, serde::Deserialize, serde::Serialize)]
		struct Container {
			date: Date,
		}

		let_assert!(Ok(serialized) = serde_yaml::to_string(&Container {
			date: Date::new(2020, Month::January, 2).unwrap(),
		}));

		assert!(serialized == "date: 2020-01-02\n");
		let_assert!(Ok(parsed) = serde_yaml::from_str::<Container>("date: 2020-01-02"));
		assert!(parsed.date == Date::new(2020, Month::January, 2).unwrap());

		let_assert!(Err(e) = serde_yaml::from_str::<Container>("date: 2021-02-29"));
		assert!(e.to_string() == "invalid day for February 2021: expected 1-28, got 29");
	}
}