use arrayvec::ArrayString;
use serde::de::{self, Deserialize, Visitor};
use serde::ser::Serialize;
use std::fmt::{self, Write};
use std::num;
use std::ops;
use std::str::FromStr;
use crate::Age;
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq)]
pub struct Date(u32);
impl Default for Date {
fn default() -> Self {
Date::from_parts(1900, 01, 01)
}
}
impl Date {
const DAY_SHIFT: usize = 0;
const DAY_MASK: u32 = 0x1f;
const MONTH_SHIFT: usize = 5;
const MONTH_MASK: u32 = 0xf;
const YEAR_SHIFT: usize = 5 + 4;
const YEAR_MASK: u32 = 0x3fff;
const DAYS_IN_MONTH: [u32; 13] = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
#[inline]
pub const fn from_parts(year: u32, month: u32, day: u32) -> Date {
Date(year << Self::YEAR_SHIFT | month << Self::MONTH_SHIFT | day << Self::DAY_SHIFT)
}
#[inline]
pub const fn year(self) -> u32 {
(self.0 >> Self::YEAR_SHIFT) & Self::YEAR_MASK
}
#[inline]
pub const fn month(self) -> u32 {
(self.0 >> Self::MONTH_SHIFT) & Self::MONTH_MASK
}
#[inline]
pub const fn day(self) -> u32 {
(self.0 >> Self::DAY_SHIFT) & Self::DAY_MASK
}
#[inline]
pub const fn monthday(self) -> u32 {
let month = self.month();
let day = self.day();
month * 100 + day
}
pub fn is_valid(self) -> bool {
let month = self.month();
if month > 12 {
return false;
}
let mut max_days = Self::DAYS_IN_MONTH[month as usize];
if month == 2 {
let year = self.year();
let is_leap = (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
if is_leap {
max_days += 1;
}
}
let day = self.day();
day > 0 && day <= max_days
}
pub fn age_on(self, date: Date) -> Result<Age, &'static str> {
if date.0 < self.0 {
return Err("Lifter was not born yet");
}
let years_u32: u32 = date.year() - self.year();
if years_u32 > u8::max_value().into() {
return Err("Calculated Age greater than 256");
}
let years = years_u32 as u8;
if date.monthday() >= self.monthday() {
Ok(Age::Exact(years))
} else {
Ok(Age::Exact(years - 1))
}
}
pub fn count_days(self) -> u32 {
let whole_previous_year_days = 365 * (self.year() - 1);
let last_maybe_leap_year = if self.month() <= 2 {
self.year() - 1
} else {
self.year()
};
let leap_4_years = last_maybe_leap_year / 4;
let leap_100_years = last_maybe_leap_year / 100;
let leap_400_years = last_maybe_leap_year / 400;
let leap_days = leap_4_years - leap_100_years + leap_400_years;
let previous_months_iter = Self::DAYS_IN_MONTH[1..(self.month() as usize)].iter();
let this_year_days: u32 = previous_months_iter.sum::<u32>() + self.day();
whole_previous_year_days + leap_days + this_year_days
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (y, m, d) = (self.year(), self.month(), self.day());
write!(f, "{:04}-{:02}-{:02}", y, m, d)
}
}
impl ops::Sub for Date {
type Output = i32;
fn sub(self, other: Date) -> i32 {
self.count_days() as i32 - other.count_days() as i32
}
}
impl Serialize for Date {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut buf = ArrayString::<10>::new();
let (y, m, d) = (self.year(), self.month(), self.day());
write!(buf, "{:04}-{:02}-{:02}", y, m, d).expect("ArrayString overflow");
serializer.serialize_str(&buf)
}
}
#[derive(Debug)]
pub enum ParseDateError {
FormatError,
InvalidMonth,
InvalidDay,
ParseIntError(num::ParseIntError),
}
impl fmt::Display for ParseDateError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParseDateError::FormatError => write!(f, "date not in the correct format"),
ParseDateError::InvalidMonth => write!(f, "invalid month"),
ParseDateError::InvalidDay => write!(f, "invalid day"),
ParseDateError::ParseIntError(ref p) => p.fmt(f),
}
}
}
impl FromStr for Date {
type Err = ParseDateError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let v: Vec<&str> = s.split('-').collect();
if v.len() != 3 || v[0].len() != 4 || v[1].len() != 2 || v[2].len() != 2 {
return Err(ParseDateError::FormatError);
}
let year: u32 = v[0].parse::<u32>().map_err(ParseDateError::ParseIntError)?;
let month: u32 = v[1].parse::<u32>().map_err(ParseDateError::ParseIntError)?;
let day: u32 = v[2].parse::<u32>().map_err(ParseDateError::ParseIntError)?;
if month == 0 || month > 12 {
return Err(ParseDateError::InvalidMonth);
}
if day == 0 || day > 31 {
return Err(ParseDateError::InvalidDay);
}
Ok(Date::from_parts(year, month, day))
}
}
struct DateVisitor;
impl<'de> Visitor<'de> for DateVisitor {
type Value = Date;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string in the format YYYY-MM-DD")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Date, E> {
Date::from_str(value).map_err(E::custom)
}
}
impl<'de> Deserialize<'de> for Date {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Date, D::Error> {
deserializer.deserialize_str(DateVisitor)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_date_basic() {
let date = "2017-03-04".parse::<Date>().unwrap();
assert_eq!(date.year(), 2017);
assert_eq!(date.month(), 3);
assert_eq!(date.day(), 4);
}
#[test]
fn test_date_errors() {
assert!("2017-03-04-05".parse::<Date>().is_err());
assert!("2017-03-004".parse::<Date>().is_err());
assert!("2017-003-04".parse::<Date>().is_err());
assert!("02017-03-04".parse::<Date>().is_err());
assert!("2017-3-4".parse::<Date>().is_err());
assert!("20170304".parse::<Date>().is_err());
assert!("".parse::<Date>().is_err());
assert!("nota-ni-nt".parse::<Date>().is_err());
assert!("2017-13-04".parse::<Date>().is_err());
assert!("2017-03-32".parse::<Date>().is_err());
assert!("2017-00-04".parse::<Date>().is_err());
assert!("2017-03-00".parse::<Date>().is_err());
}
#[test]
fn test_date_ordering() {
let d1 = "2017-01-12".parse::<Date>().unwrap();
let d2 = "2016-01-12".parse::<Date>().unwrap();
let d3 = "2017-01-13".parse::<Date>().unwrap();
let d4 = "2017-02-11".parse::<Date>().unwrap();
assert!(d1 > d2);
assert!(d2 < d1);
assert!(d3 > d1);
assert!(d4 > d1);
assert!(d3 < d4);
assert_eq!(d1 < d2, false);
assert_eq!(d2 > d1, false);
assert_eq!(d3 < d1, false);
assert_eq!(d4 < d1, false);
assert_eq!(d3 > d4, false);
let d5 = "2017-01-12".parse::<Date>().unwrap();
assert_eq!(d1, d5);
assert_ne!(d1, d4);
}
#[test]
fn test_date_display() {
let date = "2017-03-04".parse::<Date>().unwrap();
assert_eq!(format!("{}", date), "2017-03-04");
}
#[test]
fn test_age_on() {
let birthdate = "1988-02-16".parse::<Date>().unwrap();
let date = "1987-01-01".parse::<Date>().unwrap();
assert!(birthdate.age_on(date).is_err());
let date = "1988-02-15".parse::<Date>().unwrap();
assert!(birthdate.age_on(date).is_err());
let date = "1988-02-16".parse::<Date>().unwrap();
assert_eq!(birthdate.age_on(date).unwrap(), Age::Exact(0));
let date = "1988-02-16".parse::<Date>().unwrap();
assert_eq!(birthdate.age_on(date).unwrap(), Age::Exact(0));
let date = "1989-02-15".parse::<Date>().unwrap();
assert_eq!(birthdate.age_on(date).unwrap(), Age::Exact(0));
let date = "1989-02-16".parse::<Date>().unwrap();
assert_eq!(birthdate.age_on(date).unwrap(), Age::Exact(1));
let date = "2018-01-04".parse::<Date>().unwrap();
assert_eq!(birthdate.age_on(date).unwrap(), Age::Exact(29));
let date = "2018-11-03".parse::<Date>().unwrap();
assert_eq!(birthdate.age_on(date).unwrap(), Age::Exact(30));
let date = "3018-11-03".parse::<Date>().unwrap();
assert!(birthdate.age_on(date).is_err());
}
#[test]
fn test_count_days() {
let date = "0004-12-31".parse::<Date>().unwrap();
assert_eq!(date.count_days(), 366 + (3 * 365));
let date = "0100-12-31".parse::<Date>().unwrap();
assert_eq!(date.count_days(), (24 * 366) + (76 * 365));
let date = "0400-12-31".parse::<Date>().unwrap();
assert_eq!(date.count_days(), (97 * 366) + (303 * 365));
let date = "0004-02-28".parse::<Date>().unwrap();
assert_eq!(date.count_days(), (3 * 365) + 31 + 28);
let before_leap_day = "0004-02-28".parse::<Date>().unwrap();
let on_leap_day = "0004-02-29".parse::<Date>().unwrap();
let after_leap_day = "0004-03-01".parse::<Date>().unwrap();
assert_eq!(on_leap_day - before_leap_day, 1);
assert_eq!(after_leap_day - before_leap_day, 2);
}
}