1use chrono::{Datelike, FixedOffset, NaiveDate};
2
3use crate::{
4 format_timezone,
5 lexical::{InvalidDate, LexicalFormOf},
6 Datatype, DisplayYear, ParseXsd, XsdValue,
7};
8use core::fmt;
9use std::str::FromStr;
10
11#[derive(Debug, thiserror::Error)]
12#[error("invalid date value")]
13pub struct InvalidDateValue;
14
15#[derive(Debug, Clone, Copy)]
16pub struct Date {
17 pub date: NaiveDate,
18 pub offset: Option<FixedOffset>,
19}
20
21impl Date {
22 pub fn new(date: NaiveDate, offset: Option<FixedOffset>) -> Self {
23 Self { date, offset }
24 }
25}
26
27#[derive(Debug, thiserror::Error)]
28pub enum DateFromStrError {
29 #[error("invalid date syntax")]
30 Syntax(#[from] InvalidDate<String>),
31
32 #[error(transparent)]
33 Value(#[from] InvalidDateValue),
34}
35
36impl FromStr for Date {
37 type Err = DateFromStrError;
38
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 let lexical_value =
41 crate::lexical::Date::new(s).map_err(|InvalidDate(s)| InvalidDate(s.to_owned()))?;
42 lexical_value.try_as_value().map_err(Into::into)
43 }
44}
45
46impl XsdValue for Date {
47 fn datatype(&self) -> Datatype {
48 Datatype::Date
49 }
50}
51
52impl ParseXsd for Date {
53 type LexicalForm = crate::lexical::Date;
54}
55
56impl fmt::Display for Date {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 write!(
59 f,
60 "{}-{:02}-{:02}",
61 DisplayYear(self.date.year()),
62 self.date.month(),
63 self.date.day()
64 )?;
65
66 format_timezone(self.offset, f)
67 }
68}