use crate::{DateParseError, InvalidDate, InvalidDayOfMonth, InvalidDateSyntax, Month, Year, YearMonth};
use crate::util::{modulo_i16, modulo_i32};
const DAYS_IN_400_YEAR: i32 = 400 * 365 + 97;
const UNIX_EPOCH: i32 = DAYS_IN_400_YEAR * 4 + 370 * 365 + 90;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
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 {
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)?)
}
pub const fn new_const(year: Year, month: Month, day: u8) -> Result<Self, InvalidDayOfMonth> {
YearMonth::new_const(year, month).with_day(day)
}
pub const unsafe fn new_unchecked(year: Year, month: Month, day: u8) -> Self {
Self { year, month, day }
}
#[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; Date::new_unchecked(year, month, day)
}
}
#[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)
}
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)
}
pub const fn to_unix_timestamp(self) -> i64 {
let days = self.days_since_year_zero() - UNIX_EPOCH;
60 * 60 * 24 * days as i64
}
pub const fn year(self) -> Year {
self.year
}
pub const fn month(self) -> Month {
self.month
}
pub const fn day(self) -> u8 {
self.day
}
pub const fn year_month(self) -> YearMonth {
YearMonth::new_const(self.year(), self.month())
}
pub const fn day_of_year(self) -> u16 {
crate::raw::day_of_year(self.month, self.day, self.year.has_leap_day())
}
pub const fn days_remaining_in_year(self) -> u16 {
self.year.total_days() - self.day_of_year() + 1
}
#[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;
let leap_days = years / 4 - years / 100 + 1;
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
}
#[rustfmt::skip]
pub const fn from_days_since_year_zero(days: i32) -> Self {
let day_index = modulo_i32(days, DAYS_IN_400_YEAR);
let whole_cycles = (days - day_index) / DAYS_IN_400_YEAR;
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;
}
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);
let year_of_four_year_cycle = (day_of_four_year_cycle - 1) / 365;
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 };
let day_of_year = day_of_year + 1;
let year = 400 * whole_cycles + 4 * four_year_cycles + year_of_four_year_cycle;
let year = Year::new(year as i16);
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,
Err(()) => (Month::January, 1),
};
unsafe { year.with_month(month).with_day_unchecked(day_of_month) }
}
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,
}
}
}
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,
}
}
}
pub const fn add_days(self, days: i32) -> Self {
Self::from_days_since_year_zero(self.days_since_year_zero() + days)
}
pub const fn sub_days(self, days: i32) -> Self {
Self::from_days_since_year_zero(self.days_since_year_zero() - days)
}
pub const fn add_months(self, months: i32) -> Result<Self, InvalidDayOfMonth> {
self.year_month().add_months(months).with_day(self.day())
}
pub const fn sub_months(self, months: i32) -> Result<Self, InvalidDayOfMonth> {
self.year_month().add_months(months).with_day(self.day())
}
pub const fn add_years(self, years: i16) -> Result<Self, InvalidDayOfMonth> {
self.year_month().add_years(years).with_day(self.day())
}
pub const fn sub_years(self, years: i16) -> Result<Self, InvalidDayOfMonth> {
self.year_month().add_years(years).with_day(self.day())
}
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> {
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)?;
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())?;
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() {
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");
}
}