time_macros/
offset.rs

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