Skip to main content

oparl_types/
date.rs

1// SPDX-FileCopyrightText: Politik im Blick developers
2// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2
5
6use std::{fmt::Display, str::FromStr};
7
8use time::OffsetDateTime;
9
10use crate::DateTime;
11
12#[derive(
13    Debug,
14    Clone,
15    PartialEq,
16    Eq,
17    PartialOrd,
18    Ord,
19    Hash,
20    derive_more::AsRef,
21    derive_more::From,
22    derive_more::Into,
23    serde_with::SerializeDisplay,
24    serde_with::DeserializeFromStr,
25)]
26#[cfg_attr(feature = "sea-orm", derive(sea_orm::DeriveValueType))]
27pub struct Date(time::Date);
28
29impl FromStr for Date {
30    type Err = time::error::Parse;
31
32    fn from_str(s: &str) -> Result<Self, Self::Err> {
33        use time::format_description::well_known::Iso8601;
34
35        Ok(Self(time::Date::parse(s, &Iso8601::DEFAULT)?))
36    }
37}
38
39impl Display for Date {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        use time::format_description::well_known::Iso8601;
42
43        write!(f, "{}", self.0.format(&Iso8601::DATE).expect("valid date"))
44    }
45}
46
47impl From<DateTime> for Date {
48    fn from(value: DateTime) -> Self {
49        Self(OffsetDateTime::from(value).date())
50    }
51}
52
53#[cfg(feature = "sea-orm")]
54mod sea_orm_impls {
55    use sea_orm::{ActiveValue, IntoActiveValue};
56
57    use super::Date;
58
59    impl IntoActiveValue<Date> for Date {
60        fn into_active_value(self) -> ActiveValue<Date> {
61            ActiveValue::Set(self)
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use time::macros::date;
69
70    use super::Date;
71
72    #[test]
73    fn from_str() {
74        let parsed: Date = "2025-01-07"
75            .parse()
76            .expect("string must be parseable as Date");
77        assert_eq!(parsed, Date(date!(2025 - 01 - 07)));
78    }
79
80    #[test]
81    fn to_string() {
82        let d = Date(date!(2025 - 01 - 07));
83
84        assert_eq!(d.to_string(), "2025-01-07".to_string());
85    }
86}
87
88#[cfg(test)]
89mod serde_tests {
90    use pretty_assertions::assert_eq;
91    use serde_json::json;
92    use time::macros::date;
93
94    use super::Date;
95
96    #[test]
97    fn serialize() {
98        assert_eq!(json!(Date(date!(2025 - 01 - 07))), json!("2025-01-07"));
99    }
100
101    #[test]
102    fn deserialize_good() {
103        let deserialized: Date =
104            serde_json::from_value(json!("2025-01-07")).expect("json must be parseable as Date");
105
106        assert_eq!(deserialized, Date(date!(2025 - 01 - 07)));
107    }
108
109    #[test]
110    fn deserialize_bad() {
111        assert!(serde_json::from_value::<Date>(json!("xyzabcd")).is_err());
112        assert!(serde_json::from_value::<Date>(json!(true)).is_err());
113        assert!(serde_json::from_value::<Date>(json!(123)).is_err());
114    }
115}