time_macros/
offset.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
10pub(crate) struct Offset {
11    pub(crate) hours: i8,
12    pub(crate) minutes: i8,
13    pub(crate) seconds: i8,
14}
15
16pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offset, Error> {
17    if consume_any_ident(&["utc", "UTC"], chars).is_ok() {
18        return Ok(Offset {
19            hours: 0,
20            minutes: 0,
21            seconds: 0,
22        });
23    }
24
25    let sign = if consume_punct('+', chars).is_ok() {
26        1
27    } else if consume_punct('-', chars).is_ok() {
28        -1
29    } else if let Some(tree) = chars.next() {
30        return Err(Error::UnexpectedToken { tree });
31    } else {
32        return Err(Error::MissingComponent {
33            name: "sign",
34            span_start: None,
35            span_end: None,
36        });
37    };
38
39    let (hours_span, hours) = consume_number::<i8>("hour", chars)?;
40    let (mut minutes_span, mut minutes) = (Span::mixed_site(), 0);
41    let (mut seconds_span, mut seconds) = (Span::mixed_site(), 0);
42
43    if consume_punct(':', chars).is_ok() {
44        let min = consume_number::<i8>("minute", chars)?;
45        minutes_span = min.0;
46        minutes = min.1;
47
48        if consume_punct(':', chars).is_ok() {
49            let sec = consume_number::<i8>("second", chars)?;
50            seconds_span = sec.0;
51            seconds = sec.1;
52        }
53    }
54
55    if hours > 25 {
56        Err(Error::InvalidComponent {
57            name: "hour",
58            value: hours.to_string(),
59            span_start: Some(hours_span),
60            span_end: Some(hours_span),
61        })
62    } else if minutes >= Minute::per_t(Hour) {
63        Err(Error::InvalidComponent {
64            name: "minute",
65            value: minutes.to_string(),
66            span_start: Some(minutes_span),
67            span_end: Some(minutes_span),
68        })
69    } else if seconds >= Second::per_t(Minute) {
70        Err(Error::InvalidComponent {
71            name: "second",
72            value: seconds.to_string(),
73            span_start: Some(seconds_span),
74            span_end: Some(seconds_span),
75        })
76    } else {
77        Ok(Offset {
78            hours: sign * hours,
79            minutes: sign * minutes,
80            seconds: sign * seconds,
81        })
82    }
83}
84
85impl ToTokenTree for Offset {
86    fn into_token_tree(self) -> TokenTree {
87        quote_group! {{
88            const OFFSET: ::time::UtcOffset = unsafe {
89                ::time::UtcOffset::__from_hms_unchecked(
90                    #(self.hours),
91                    #(self.minutes),
92                    #(self.seconds),
93                )
94            };
95            OFFSET
96        }}
97    }
98}