chj_rustbin/time/
naive_date_time_without_year.rs

1use std::convert::TryInto;
2use std::fmt::Display;
3
4use crate::parse::parse_error::{
5    Backing, IntoOwningBacking, NoContext, ParseContext, ParseError,
6    StringParseContext,
7};
8use crate::parse_error;
9use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
10
11#[derive(Debug, Clone)]
12pub struct NaiveDateTimeWithoutYear<C: ParseContext> {
13    pub context: C,
14    pub month: u8,
15    pub day: u8,
16    pub hour: u8,
17    pub minute: u8,
18    pub second: u8,
19}
20
21//Can't do this because C includes itself, right?
22// impl<'s, C, B> IntoOwningParseContext<B> for NaiveDateTimeWithoutYear<C>
23// where C: ParseContext,
24//       B: Backing,
25//       StringParseContext<B>: From<C>,
26// {
27//     type Owning = NaiveDateTimeWithoutYear<StringParseContext<B>>;
28
29impl<'s, B: Backing> IntoOwningBacking<B::Owned>
30    for NaiveDateTimeWithoutYear<StringParseContext<&'s B>>
31where
32    &'s B: Backing,
33    B::Owned: Backing,
34{
35    type Owning = NaiveDateTimeWithoutYear<StringParseContext<B::Owned>>;
36
37    fn into_owning_backing(self) -> Self::Owning {
38        let Self {
39            context,
40            month,
41            day,
42            hour,
43            minute,
44            second,
45        } = self;
46        NaiveDateTimeWithoutYear {
47            context: context.into_owning_backing(),
48            month,
49            day,
50            hour,
51            minute,
52            second,
53        }
54    }
55}
56
57impl<C: ParseContext> NaiveDateTimeWithoutYear<C> {
58    pub fn with_year(self, year: i32) -> Result<NaiveDateTime, ParseError<C>> {
59        let NaiveDateTimeWithoutYear {
60            context,
61            month,
62            day,
63            hour,
64            minute,
65            second,
66        } = self;
67        (|| -> Result<NaiveDateTime, String> {
68            let nd = NaiveDate::from_ymd_opt(year, month.into(), day.into())
69                .ok_or_else(|| format!("invalid month/day in year {year}"))?;
70            let nt = NaiveTime::from_hms_opt(
71                hour.into(),
72                minute.into(),
73                second.into(),
74            )
75            .ok_or_else(|| {
76                format!("invalid hh:mm:ss numbers {hour}:{minute}:{second}")
77            })?;
78            Ok(NaiveDateTime::new(nd, nt))
79        })()
80        .map_err(|message| {
81            parse_error! {
82                message, context
83            }
84        })
85    }
86
87    pub fn into_naive_date_time(
88        self,
89    ) -> anyhow::Result<NaiveDateTime, ParseError<C>> {
90        Err(
91            parse_error! { message: "date is missing year".into(), context: self.context },
92        )
93    }
94
95    /// Whether `self` come before or after `other` or is the same,
96    /// assuming the same year and that the dates are valid for that
97    /// assumed year. `NaiveDateTimeWithoutYear` is *not* implementing
98    /// Ord / PartialOrd because those also require PartialEq and it's
99    /// unclear whether that should include position or not, also
100    /// ordering is easily misunderstood, too, since year is actually
101    /// unknown.
102    pub fn compare<C2: ParseContext>(
103        &self,
104        other: &NaiveDateTimeWithoutYear<C2>,
105    ) -> std::cmp::Ordering {
106        self.month.cmp(&other.month).then(
107            self.day.cmp(&other.day).then(
108                self.hour.cmp(&other.hour).then(
109                    self.minute
110                        .cmp(&other.minute)
111                        .then(self.second.cmp(&other.second)),
112                ),
113            ),
114        )
115    }
116}
117
118impl<C: ParseContext> Display for NaiveDateTimeWithoutYear<C> {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        let Self {
121            context: _,
122            month,
123            day,
124            hour,
125            minute,
126            second,
127        } = self;
128        f.write_fmt(format_args!(
129            "{month:02}-{day:02} {hour:02}:{minute:02}:{second:02}"
130        ))
131    }
132}
133
134/// Note: uses position 0 always!
135impl From<&NaiveDateTime> for NaiveDateTimeWithoutYear<NoContext> {
136    fn from(value: &NaiveDateTime) -> Self {
137        Self {
138            context: NoContext,
139            month: value.month().try_into().expect("always fits"),
140            day: value.day().try_into().expect("always fits"),
141            hour: value.hour().try_into().expect("always fits"),
142            minute: value.minute().try_into().expect("always fits"),
143            second: value.second().try_into().expect("always fits"),
144        }
145    }
146}