time_fmt/format/
time_format_item.rs

1use std::slice::SliceIndex;
2
3use thiserror::Error;
4use time::format_description::{modifier, Component, FormatItem};
5
6use super::spec_parser::Collector;
7
8#[derive(Error, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
9#[non_exhaustive]
10pub enum Error {
11    #[error("Unknown specifier `%{0}`")]
12    UnknownSpecifier(char),
13    #[error("No FormatItem that represent {0}")]
14    NoCorrespondingFormatItem(&'static str),
15}
16
17struct ToFormatItemCollector<'a> {
18    fmt: &'a [u8],
19    items: Vec<FormatItem<'a>>,
20}
21
22impl<'a> ToFormatItemCollector<'a> {
23    fn new(fmt: &'a [u8]) -> Self {
24        Self {
25            fmt,
26            items: Default::default(),
27        }
28    }
29}
30
31impl<'a> Collector for ToFormatItemCollector<'a> {
32    type Output = Vec<FormatItem<'a>>;
33    type Error = Error;
34
35    #[inline]
36    fn day_of_week_name_short(&mut self) -> Result<(), Self::Error> {
37        let mut modifier = modifier::Weekday::default();
38        modifier.repr = modifier::WeekdayRepr::Short;
39        self.items
40            .push(FormatItem::Component(Component::Weekday(modifier)));
41        Ok(())
42    }
43
44    #[inline]
45    fn day_of_week_name_long(&mut self) -> Result<(), Self::Error> {
46        let mut modifier = modifier::Weekday::default();
47        modifier.repr = modifier::WeekdayRepr::Long;
48        self.items
49            .push(FormatItem::Component(Component::Weekday(modifier)));
50        Ok(())
51    }
52
53    #[inline]
54    fn month_name_short(&mut self) -> Result<(), Self::Error> {
55        let mut modifier = modifier::Month::default();
56        modifier.repr = modifier::MonthRepr::Short;
57        self.items
58            .push(FormatItem::Component(Component::Month(modifier)));
59        Ok(())
60    }
61
62    #[inline]
63    fn month_name_long(&mut self) -> Result<(), Self::Error> {
64        let mut modifier = modifier::Month::default();
65        modifier.repr = modifier::MonthRepr::Long;
66        self.items
67            .push(FormatItem::Component(Component::Month(modifier)));
68        Ok(())
69    }
70
71    #[inline]
72    fn year_prefix(&mut self) -> Result<(), Self::Error> {
73        Err(Self::Error::NoCorrespondingFormatItem("%C"))
74    }
75
76    #[inline]
77    fn day_of_month(&mut self) -> Result<(), Self::Error> {
78        let mut modifier = modifier::Day::default();
79        modifier.padding = modifier::Padding::Zero;
80        self.items
81            .push(FormatItem::Component(Component::Day(modifier)));
82        Ok(())
83    }
84
85    #[inline]
86    fn day_of_month_blank(&mut self) -> Result<(), Self::Error> {
87        let mut modifier = modifier::Day::default();
88        modifier.padding = modifier::Padding::Space;
89        self.items
90            .push(FormatItem::Component(Component::Day(modifier)));
91        Ok(())
92    }
93
94    #[inline]
95    fn iso8601_week_based_year_suffix(&mut self) -> Result<(), Self::Error> {
96        let mut modifier = modifier::Year::default();
97        modifier.iso_week_based = true;
98        modifier.repr = modifier::YearRepr::LastTwo;
99        self.items
100            .push(FormatItem::Component(Component::Year(modifier)));
101        Ok(())
102    }
103
104    #[inline]
105    fn iso8601_week_based_year(&mut self) -> Result<(), Self::Error> {
106        let mut modifier = modifier::Year::default();
107        modifier.iso_week_based = true;
108        self.items
109            .push(FormatItem::Component(Component::Year(modifier)));
110        Ok(())
111    }
112
113    #[inline]
114    fn hour_of_day(&mut self) -> Result<(), Self::Error> {
115        let modifier = modifier::Hour::default();
116        self.items
117            .push(FormatItem::Component(Component::Hour(modifier)));
118        Ok(())
119    }
120
121    #[inline]
122    fn hour_of_day_12(&mut self) -> Result<(), Self::Error> {
123        let mut modifier = modifier::Hour::default();
124        modifier.is_12_hour_clock = true;
125        self.items
126            .push(FormatItem::Component(Component::Hour(modifier)));
127        Ok(())
128    }
129
130    #[inline]
131    fn day_of_year(&mut self) -> Result<(), Self::Error> {
132        let modifier = modifier::Ordinal::default();
133        self.items
134            .push(FormatItem::Component(Component::Ordinal(modifier)));
135        Ok(())
136    }
137
138    #[inline]
139    fn hour_of_day_blank(&mut self) -> Result<(), Self::Error> {
140        let mut modifier = modifier::Hour::default();
141        modifier.padding = modifier::Padding::Space;
142        self.items
143            .push(FormatItem::Component(Component::Hour(modifier)));
144        Ok(())
145    }
146
147    #[inline]
148    fn hour_of_day_12_blank(&mut self) -> Result<(), Self::Error> {
149        let mut modifier = modifier::Hour::default();
150        modifier.padding = modifier::Padding::Space;
151        modifier.is_12_hour_clock = true;
152        self.items
153            .push(FormatItem::Component(Component::Hour(modifier)));
154        Ok(())
155    }
156
157    #[inline]
158    fn month_of_year(&mut self) -> Result<(), Self::Error> {
159        let mut modifier = modifier::Month::default();
160        modifier.repr = modifier::MonthRepr::Numerical;
161        self.items
162            .push(FormatItem::Component(Component::Month(modifier)));
163        Ok(())
164    }
165
166    #[inline]
167    fn minute_of_hour(&mut self) -> Result<(), Self::Error> {
168        let modifier = modifier::Minute::default();
169        self.items
170            .push(FormatItem::Component(Component::Minute(modifier)));
171        Ok(())
172    }
173
174    #[inline]
175    fn ampm(&mut self) -> Result<(), Self::Error> {
176        let mut modifier = modifier::Period::default();
177        modifier.is_uppercase = true;
178        self.items
179            .push(FormatItem::Component(Component::Period(modifier)));
180        Ok(())
181    }
182
183    #[inline]
184    fn ampm_lower(&mut self) -> Result<(), Self::Error> {
185        let mut modifier = modifier::Period::default();
186        modifier.is_uppercase = false;
187        self.items
188            .push(FormatItem::Component(Component::Period(modifier)));
189        Ok(())
190    }
191
192    #[inline]
193    fn second_of_minute(&mut self) -> Result<(), Self::Error> {
194        let modifier = modifier::Second::default();
195        self.items
196            .push(FormatItem::Component(Component::Second(modifier)));
197        Ok(())
198    }
199
200    #[inline]
201    fn nanosecond_of_second(&mut self) -> Result<(), Self::Error> {
202        let modifier = modifier::Subsecond::default();
203        self.items
204            .push(FormatItem::Component(Component::Subsecond(modifier)));
205        Ok(())
206    }
207
208    #[inline]
209    fn day_of_week_from_monday_as_1(&mut self) -> Result<(), Self::Error> {
210        let mut modifier = modifier::Weekday::default();
211        modifier.repr = modifier::WeekdayRepr::Monday;
212        modifier.one_indexed = true;
213        self.items
214            .push(FormatItem::Component(Component::Weekday(modifier)));
215        Ok(())
216    }
217
218    #[inline]
219    fn week_number_of_current_year_start_sunday(&mut self) -> Result<(), Self::Error> {
220        let mut modifier = modifier::WeekNumber::default();
221        modifier.repr = modifier::WeekNumberRepr::Sunday;
222        self.items
223            .push(FormatItem::Component(Component::WeekNumber(modifier)));
224        Ok(())
225    }
226
227    #[inline]
228    fn iso8601_week_number(&mut self) -> Result<(), Self::Error> {
229        let mut modifier = modifier::WeekNumber::default();
230        modifier.repr = modifier::WeekNumberRepr::Iso;
231        self.items
232            .push(FormatItem::Component(Component::WeekNumber(modifier)));
233        Ok(())
234    }
235
236    #[inline]
237    fn day_of_week_from_sunday_as_0(&mut self) -> Result<(), Self::Error> {
238        let mut modifier = modifier::Weekday::default();
239        modifier.repr = modifier::WeekdayRepr::Sunday;
240        modifier.one_indexed = false;
241        self.items
242            .push(FormatItem::Component(Component::Weekday(modifier)));
243        Ok(())
244    }
245
246    #[inline]
247    fn week_number_of_current_year_start_monday(&mut self) -> Result<(), Self::Error> {
248        let mut modifier = modifier::WeekNumber::default();
249        modifier.repr = modifier::WeekNumberRepr::Monday;
250        self.items
251            .push(FormatItem::Component(Component::WeekNumber(modifier)));
252        Ok(())
253    }
254
255    #[inline]
256    fn year_suffix(&mut self) -> Result<(), Self::Error> {
257        let mut modifier = modifier::Year::default();
258        modifier.repr = modifier::YearRepr::LastTwo;
259        self.items
260            .push(FormatItem::Component(Component::Year(modifier)));
261        Ok(())
262    }
263
264    #[inline]
265    fn year(&mut self) -> Result<(), Self::Error> {
266        let modifier = modifier::Year::default();
267        self.items
268            .push(FormatItem::Component(Component::Year(modifier)));
269        Ok(())
270    }
271
272    #[inline]
273    fn timezone(&mut self) -> Result<(), Self::Error> {
274        let mut modifier = modifier::OffsetHour::default();
275        modifier.sign_is_mandatory = true;
276        self.items
277            .push(FormatItem::Component(Component::OffsetHour(modifier)));
278        let modifier = modifier::OffsetMinute::default();
279        self.items
280            .push(FormatItem::Component(Component::OffsetMinute(modifier)));
281        Ok(())
282    }
283
284    #[inline]
285    fn timezone_name(&mut self) -> Result<(), Self::Error> {
286        Err(Self::Error::NoCorrespondingFormatItem("timezone name"))
287    }
288
289    #[inline]
290    fn static_str(&mut self, s: &'static str) -> Result<(), Self::Error> {
291        self.items.push(FormatItem::Literal(s.as_bytes()));
292        Ok(())
293    }
294
295    #[inline]
296    fn literal(
297        &mut self,
298        _lit: &str,
299        fmt_span: impl SliceIndex<[u8], Output = [u8]>,
300    ) -> Result<(), Self::Error> {
301        self.items.push(FormatItem::Literal(&self.fmt[fmt_span]));
302        Ok(())
303    }
304
305    #[inline]
306    fn unknown(&mut self, specifier: char) -> Result<(), Self::Error> {
307        Err(Self::Error::UnknownSpecifier(specifier))
308    }
309
310    #[inline]
311    fn output(self) -> Result<Self::Output, Self::Error> {
312        Ok(self.items)
313    }
314}
315
316pub fn parse_to_format_item(fmt: &str) -> Result<Vec<FormatItem>, Error> {
317    let collector = ToFormatItemCollector::new(fmt.as_bytes());
318    super::spec_parser::parse_conversion_specifications(fmt, collector)
319}
320
321#[cfg(test)]
322mod tests {
323    use time::macros::datetime;
324
325    use super::parse_to_format_item;
326
327    #[test]
328    fn it_works() -> Result<(), super::Error> {
329        assert_eq!(
330            parse_to_format_item("%Y-%m-%d")?,
331            parse_to_format_item("%F")?,
332        );
333        Ok(())
334    }
335
336    #[test]
337    fn parse_primitive_datetime() -> Result<(), Box<dyn std::error::Error>> {
338        let format_items = parse_to_format_item("%Y-%m-%d %H:%M:%S")?;
339        assert_eq!(
340            datetime!(2012-05-21 12:09:14).format(&format_items)?,
341            "2012-05-21 12:09:14"
342        );
343        Ok(())
344    }
345
346    #[test]
347    fn parse_offset_datetime() -> Result<(), Box<dyn std::error::Error>> {
348        let format_items = parse_to_format_item("%Y-%m-%d %H:%M:%S %z")?;
349        assert_eq!(
350            datetime!(2012-05-21 12:09:14 +9:00).format(&format_items)?,
351            "2012-05-21 12:09:14 +0900"
352        );
353        Ok(())
354    }
355}