use std::fmt;
#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
pub struct Date(u32);
impl Date {
pub fn read() -> Option<Date> {
::get_version_and_date()
.and_then(|(_, date)| date)
.and_then(|date| Date::parse(&date))
}
pub fn parse(date: &str) -> Option<Date> {
let mut ymd = [0u16; 3];
for (i, split) in date.split('-').map(|s| s.parse::<u16>()).enumerate() {
ymd[i] = match (i, split) {
(3, _) | (_, Err(_)) => return None,
(_, Ok(v)) => v,
};
}
let (year, month, day) = (ymd[0], ymd[1], ymd[2]);
if year == 0 || month == 0 || month > 12 || day == 0 || day > 31 {
return None;
}
Some(Date::from_ymd(year, month as u8, day as u8))
}
pub fn from_ymd(year: u16, month: u8, day: u8) -> Date {
let year = (year as u32) << 9;
let month = ((month as u32) & 0xF) << 5;
let day = (day as u32) & 0x1F;
Date(year | month | day)
}
fn to_ymd(&self) -> (u16, u8, u8) {
let y = self.0 >> 9;
let m = (self.0 >> 5) & 0xF;
let d = self.0 & 0x1F;
(y as u16, m as u8, d as u8)
}
pub fn at_least(&self, date: &str) -> bool {
Date::parse(date)
.map(|date| self >= &date)
.unwrap_or(false)
}
pub fn at_most(&self, date: &str) -> bool {
Date::parse(date)
.map(|date| self <= &date)
.unwrap_or(false)
}
pub fn exactly(&self, date: &str) -> bool {
Date::parse(date)
.map(|date| self == &date)
.unwrap_or(false)
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (y, m, d) = self.to_ymd();
write!(f, "{}-{:02}-{:02}", y, m, d)
}
}
#[cfg(test)]
mod tests {
use super::Date;
macro_rules! reflexive_display {
($string:expr) => (
assert_eq!(Date::parse($string).unwrap().to_string(), $string);
)
}
#[test]
fn display() {
reflexive_display!("2019-05-08");
reflexive_display!("2000-01-01");
reflexive_display!("2000-12-31");
reflexive_display!("2090-12-31");
reflexive_display!("1999-02-19");
reflexive_display!("9999-12-31");
}
}