time_macros/
time.rs

1use std::iter::Peekable;
2
3use proc_macro::{token_stream, Span, TokenTree};
4use time_core::convert::*;
5
6use crate::helpers::{consume_any_ident, consume_number, consume_punct};
7use crate::to_tokens::ToTokenTree;
8use crate::Error;
9
10enum Period {
11    Am,
12    Pm,
13    _24,
14}
15
16pub(crate) struct Time {
17    pub(crate) hour: u8,
18    pub(crate) minute: u8,
19    pub(crate) second: u8,
20    pub(crate) nanosecond: u32,
21}
22
23pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time, Error> {
24    fn consume_period(chars: &mut Peekable<token_stream::IntoIter>) -> (Option<Span>, Period) {
25        if let Ok(span) = consume_any_ident(&["am", "AM"], chars) {
26            (Some(span), Period::Am)
27        } else if let Ok(span) = consume_any_ident(&["pm", "PM"], chars) {
28            (Some(span), Period::Pm)
29        } else {
30            (None, Period::_24)
31        }
32    }
33
34    let (hour_span, hour) = consume_number("hour", chars)?;
35
36    let ((minute_span, minute), (second_span, second), (period_span, period)) =
37        match consume_period(chars) {
38            // Nothing but the 12-hour clock hour and AM/PM
39            (period_span @ Some(_), period) => (
40                (Span::mixed_site(), 0),
41                (Span::mixed_site(), 0.),
42                (period_span, period),
43            ),
44            (None, _) => {
45                consume_punct(':', chars)?;
46                let (minute_span, minute) = consume_number::<u8>("minute", chars)?;
47                let (second_span, second): (_, f64) = if consume_punct(':', chars).is_ok() {
48                    consume_number("second", chars)?
49                } else {
50                    (Span::mixed_site(), 0.)
51                };
52                let (period_span, period) = consume_period(chars);
53                (
54                    (minute_span, minute),
55                    (second_span, second),
56                    (period_span, period),
57                )
58            }
59        };
60
61    let hour = match (hour, period) {
62        (0, Period::Am | Period::Pm) => {
63            return Err(Error::InvalidComponent {
64                name: "hour",
65                value: hour.to_string(),
66                span_start: Some(hour_span),
67                span_end: Some(period_span.unwrap_or(hour_span)),
68            });
69        }
70        (12, Period::Am) => 0,
71        (12, Period::Pm) => 12,
72        (hour, Period::Am | Period::_24) => hour,
73        (hour, Period::Pm) => hour + 12,
74    };
75
76    if hour >= Hour::per(Day) {
77        Err(Error::InvalidComponent {
78            name: "hour",
79            value: hour.to_string(),
80            span_start: Some(hour_span),
81            span_end: Some(period_span.unwrap_or(hour_span)),
82        })
83    } else if minute >= Minute::per(Hour) {
84        Err(Error::InvalidComponent {
85            name: "minute",
86            value: minute.to_string(),
87            span_start: Some(minute_span),
88            span_end: Some(minute_span),
89        })
90    } else if second >= Second::per(Minute) as _ {
91        Err(Error::InvalidComponent {
92            name: "second",
93            value: second.to_string(),
94            span_start: Some(second_span),
95            span_end: Some(second_span),
96        })
97    } else {
98        Ok(Time {
99            hour,
100            minute,
101            second: second.trunc() as _,
102            nanosecond: (second.fract() * Nanosecond::per(Second) as f64).round() as _,
103        })
104    }
105}
106
107impl ToTokenTree for Time {
108    fn into_token_tree(self) -> TokenTree {
109        quote_group! {{
110            const TIME: ::time::Time = unsafe {
111                ::time::Time::__from_hms_nanos_unchecked(
112                    #(self.hour),
113                    #(self.minute),
114                    #(self.second),
115                    #(self.nanosecond),
116                )
117            };
118            TIME
119        }}
120    }
121}