use std::fmt;
use crate::DateTime;
const BILLECTA_DATE_FORMAT: &[time::format_description::FormatItem] =
time::macros::format_description!("[year]-[month]-[day]");
const BILLECTA_ALTERNATIVE_DATE_FORMAT: &[time::format_description::FormatItem] =
time::macros::format_description!("[year][month][day]");
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct Date(pub time::Date);
impl Date {
pub fn ymd(year: i32, month: u8, day: u8) -> Self {
time::Date::from_calendar_date(year, month.try_into().expect("invalid month"), day)
.expect("invalid date")
.into()
}
pub fn with_hms_stockholm(self, hour: u8, minute: u8, second: u8) -> DateTime {
DateTime::from(self.0.with_hms(hour, minute, second).expect("invalid time"))
}
pub fn next_day(self) -> Self {
Self(self.0.next_day().expect("Date max range exceeded"))
}
pub fn previous_day(self) -> Self {
Self(self.0.previous_day().expect("Date min range exceeded"))
}
pub fn year(&self) -> i32 {
self.0.year()
}
pub fn month(&self) -> u8 {
self.0.month() as u8
}
pub fn day(&self) -> u8 {
self.0.day()
}
}
impl From<time::Date> for Date {
fn from(d: time::Date) -> Self {
Self(d)
}
}
impl From<Date> for time::Date {
fn from(d: Date) -> Self {
d.0
}
}
struct Visitor;
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{:04}-{:02}-{:02}",
self.year(),
self.month(),
self.day()
)
}
}
impl std::str::FromStr for Date {
type Err = time::error::Parse;
fn from_str(s: &str) -> Result<Self, Self::Err> {
time::Date::parse(s, &BILLECTA_DATE_FORMAT)
.or_else(|_| time::Date::parse(s, BILLECTA_ALTERNATIVE_DATE_FORMAT))
.map(Date)
}
}
impl serde::de::Visitor<'_> for Visitor {
type Value = Date;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(r#"a date string on the form: "2013-12-30""#)
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
s.parse::<Date>().map_err(E::custom)
}
}
impl<'de> serde::Deserialize<'de> for Date {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
deserializer.deserialize_str(Visitor)
}
}
impl serde::Serialize for Date {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = self.0.to_string();
serializer.serialize_str(&s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display() {
assert_eq!("2024-04-10", Date::ymd(2024, 4, 10).to_string());
assert_eq!("2024-01-01", Date::ymd(2024, 1, 1).to_string());
assert_eq!("2024-12-31", Date::ymd(2024, 12, 31).to_string());
}
#[test]
fn parse() {
assert_eq!(
Date::ymd(2024, 4, 10),
"2024-04-10".parse::<Date>().expect("parsing"),
);
assert_eq!(
Date::ymd(2024, 1, 1),
"2024-01-01".parse::<Date>().expect("parsing"),
);
assert_eq!(
Date::ymd(2024, 12, 31),
"2024-12-31".parse::<Date>().expect("parsing"),
);
assert_eq!(
Date::ymd(2024, 4, 10),
"20240410".parse::<Date>().expect("parsing"),
);
assert_eq!(
Date::ymd(2024, 1, 1),
"20240101".parse::<Date>().expect("parsing"),
);
assert_eq!(
Date::ymd(2024, 12, 31),
"20241231".parse::<Date>().expect("parsing"),
);
}
#[test]
fn parse_json() {
assert_eq!(
Date::ymd(2024, 12, 31),
serde_json::from_str::<Date>(r#""2024-12-31""#).expect("deser")
);
assert_eq!(
Date::ymd(2024, 1, 1),
serde_json::from_str::<Date>(r#""2024-01-01""#).expect("deser")
);
assert_eq!(
Date::ymd(2024, 4, 10),
serde_json::from_str::<Date>(r#""2024-04-10""#).expect("deser")
);
assert_eq!(
Date::ymd(2024, 12, 31),
serde_json::from_str::<Date>(r#""20241231""#).expect("deser")
);
assert_eq!(
Date::ymd(2024, 1, 1),
serde_json::from_str::<Date>(r#""20240101""#).expect("deser")
);
assert_eq!(
Date::ymd(2024, 4, 10),
serde_json::from_str::<Date>(r#""20240410""#).expect("deser")
);
}
#[test]
fn fmt_json() {
assert_eq!(
r#""2024-12-31""#,
serde_json::to_string(&Date::ymd(2024, 12, 31)).expect("ser"),
);
assert_eq!(
r#""2024-01-01""#,
serde_json::to_string(&Date::ymd(2024, 1, 1)).expect("ser"),
);
assert_eq!(
r#""2024-04-10""#,
serde_json::to_string(&Date::ymd(2024, 4, 10)).expect("ser"),
);
}
}