| use std::iter::Peekable; |
| |
| use num_conv::prelude::*; |
| use proc_macro::{token_stream, Span, TokenTree}; |
| use time_core::convert::*; |
| |
| use crate::helpers::{consume_any_ident, consume_number, consume_punct}; |
| use crate::to_tokens::ToTokenTree; |
| use crate::Error; |
| |
| pub(crate) struct Offset { |
| pub(crate) hours: i8, |
| pub(crate) minutes: i8, |
| pub(crate) seconds: i8, |
| } |
| |
| pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offset, Error> { |
| if consume_any_ident(&["utc", "UTC"], chars).is_ok() { |
| return Ok(Offset { |
| hours: 0, |
| minutes: 0, |
| seconds: 0, |
| }); |
| } |
| |
| let sign = if consume_punct('+', chars).is_ok() { |
| 1 |
| } else if consume_punct('-', chars).is_ok() { |
| -1 |
| } else if let Some(tree) = chars.next() { |
| return Err(Error::UnexpectedToken { tree }); |
| } else { |
| return Err(Error::MissingComponent { |
| name: "sign", |
| span_start: None, |
| span_end: None, |
| }); |
| }; |
| |
| let (hours_span, hours) = consume_number::<i8>("hour", chars)?; |
| let (mut minutes_span, mut minutes) = (Span::mixed_site(), 0); |
| let (mut seconds_span, mut seconds) = (Span::mixed_site(), 0); |
| |
| if consume_punct(':', chars).is_ok() { |
| let min = consume_number::<i8>("minute", chars)?; |
| minutes_span = min.0; |
| minutes = min.1; |
| |
| if consume_punct(':', chars).is_ok() { |
| let sec = consume_number::<i8>("second", chars)?; |
| seconds_span = sec.0; |
| seconds = sec.1; |
| } |
| } |
| |
| if hours > 25 { |
| Err(Error::InvalidComponent { |
| name: "hour", |
| value: hours.to_string(), |
| span_start: Some(hours_span), |
| span_end: Some(hours_span), |
| }) |
| } else if minutes >= Minute::per(Hour).cast_signed() { |
| Err(Error::InvalidComponent { |
| name: "minute", |
| value: minutes.to_string(), |
| span_start: Some(minutes_span), |
| span_end: Some(minutes_span), |
| }) |
| } else if seconds >= Second::per(Minute).cast_signed() { |
| Err(Error::InvalidComponent { |
| name: "second", |
| value: seconds.to_string(), |
| span_start: Some(seconds_span), |
| span_end: Some(seconds_span), |
| }) |
| } else { |
| Ok(Offset { |
| hours: sign * hours, |
| minutes: sign * minutes, |
| seconds: sign * seconds, |
| }) |
| } |
| } |
| |
| impl ToTokenTree for Offset { |
| fn into_token_tree(self) -> TokenTree { |
| quote_group! {{ |
| const OFFSET: ::time::UtcOffset = unsafe { |
| ::time::UtcOffset::__from_hms_unchecked( |
| #(self.hours), |
| #(self.minutes), |
| #(self.seconds), |
| ) |
| }; |
| OFFSET |
| }} |
| } |
| } |