use chrono::Datelike;
use std::fmt;
pub struct VeekDate {
year: u32,
veek: u32,
day: u32,
}
impl VeekDate {
pub fn from_yvd(year: u32, veek: u32, day: u32) -> Option<Self> {
if year < 1000 || year > 9999 || veek < 1 || veek > 53 || day < 1 || day > 7 {
return None;
}
Some(VeekDate { year, veek, day })
}
pub fn from_year_ordinal_day(year: u32, ordinal_day: u32) -> Option<Self> {
let veek = ((ordinal_day as f64 - 0.1) / 7.0).floor() as u32 + 1;
let day = ((ordinal_day as f64 - 0.1) % 7.0).round() as u32;
Self::from_yvd(year, veek, day)
}
pub fn from_naive_date(nd: chrono::NaiveDate) -> Option<Self> {
Self::from_year_ordinal_day(nd.year() as u32, nd.ordinal() as u32)
}
pub fn from_ymd(year: u32, month: u32, day: u32) -> Option<Self> {
match chrono::NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32) {
Some(nd) => Self::from_naive_date(nd),
None => None,
}
}
pub fn from_str(s: &str) -> Option<Self> {
if s.len() != 12 {
return None;
};
let year = s.get(0..4)?.parse::<u32>().ok()?;
let year_unit = s.get(4..6)?;
if year_unit != "c " {
return None;
};
let veek = s.get(6..8)?.parse::<u32>().ok()?;
let veek_unit = s.get(8..10)?;
if veek_unit != "v " {
return None;
};
let day = s.get(10..11)?.parse::<u32>().ok()?;
let day_unit = s.get(11..12)?;
if day_unit != "d" {
return None;
};
Self::from_yvd(year, veek, day)
}
pub fn year(&self) -> u32 {
self.year
}
pub fn veek(&self) -> u32 {
self.veek
}
pub fn day(&self) -> u32 {
self.day
}
pub fn to_naive_date(&self) -> Option<chrono::NaiveDate> {
chrono::NaiveDate::from_yo_opt(self.year as i32, ((self.veek - 1) * 7 + self.day) as u32)
}
}
impl fmt::Display for VeekDate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, r#"{:04}c {:02}v {:01}d"#, self.year, self.veek, self.day)
}
}
#[cfg(test)]
mod test {
use chrono::NaiveDate;
#[test]
pub fn t01_naive_date_to_veek_date() {
let nd = NaiveDate::from_ymd(2021, 02, 28);
let veeks = super::VeekDate::from_naive_date(nd).unwrap();
assert_eq!(veeks.to_string(), "2021c 09v 3d");
}
#[test]
pub fn t02_naive_date_to_veek_date() {
let nd = NaiveDate::from_ymd(2021, 05, 01);
let veeks = super::VeekDate::from_naive_date(nd).unwrap();
assert_eq!(veeks.to_string(), "2021c 18v 2d");
}
#[test]
pub fn t03_naive_date_to_veek_date() {
let nd = NaiveDate::from_ymd(2021, 12, 25);
let veeks = super::VeekDate::from_naive_date(nd).unwrap();
assert_eq!(veeks.to_string(), "2021c 52v 2d");
}
#[test]
pub fn t04_veek_to_naive_date_opt() {
let veek = super::VeekDate::from_str("2021c 09v 3d").unwrap();
let nd = veek.to_naive_date().unwrap();
assert_eq!(nd, NaiveDate::from_ymd(2021, 02, 28));
}
#[test]
pub fn t05_veek_to_naive_date_opt() {
let veek = super::VeekDate::from_str("2021c 52v 2d").unwrap();
let nd = veek.to_naive_date().unwrap();
assert_eq!(nd, NaiveDate::from_ymd(2021, 12, 25));
}
#[test]
#[should_panic]
pub fn t06_veek_to_naive_date_opt_should_panic() {
let _veek = super::VeekDate::from_str("2021c-09v-3d").unwrap();
}
}