Skip to main content

time_macros/helpers/
mod.rs

1#[cfg(any(feature = "formatting", feature = "parsing"))]
2mod string;
3
4use std::iter::Peekable;
5use std::str::FromStr;
6
7use num_conv::prelude::*;
8use proc_macro::{Span, TokenTree, token_stream};
9use time_core::util::{days_in_year, is_leap_year};
10
11use crate::Error;
12use crate::date::MAX_YEAR;
13
14#[cfg(any(feature = "formatting", feature = "parsing"))]
15pub(crate) fn get_string_literal(
16    permit_byte_strings: bool,
17    mut tokens: impl Iterator<Item = TokenTree>,
18) -> Result<(Span, Vec<u8>), Error> {
19    match (tokens.next(), tokens.next()) {
20        (Some(TokenTree::Literal(literal)), None) => string::parse(permit_byte_strings, &literal),
21        (Some(tree), None) => Err(Error::ExpectedString {
22            span_start: Some(tree.span()),
23            span_end: Some(tree.span()),
24        }),
25        (_, Some(tree)) => Err(Error::UnexpectedToken { tree }),
26        (None, None) => Err(Error::ExpectedString {
27            span_start: None,
28            span_end: None,
29        }),
30    }
31}
32
33pub(crate) fn parse_number<T: FromStr>(
34    component_name: &'static str,
35    chars: &str,
36) -> Result<T, Error> {
37    chars
38        .replace('_', "")
39        .parse()
40        .map_err(|_| Error::InvalidComponent {
41            name: component_name,
42            value: chars.to_string(),
43            span_start: None,
44            span_end: None,
45        })
46}
47
48pub(crate) fn consume_number<T: FromStr>(
49    component_name: &'static str,
50    chars: &mut Peekable<token_stream::IntoIter>,
51) -> Result<(Span, T), Error> {
52    match chars.next() {
53        Some(TokenTree::Literal(literal)) => {
54            let span = literal.span();
55            match parse_number(component_name, &literal.to_string()) {
56                Ok(val) => Ok((span, val)),
57                Err(Error::InvalidComponent {
58                    name,
59                    value,
60                    span_start: _,
61                    span_end: _,
62                }) => Err(Error::InvalidComponent {
63                    name,
64                    value,
65                    span_start: Some(span.start()),
66                    span_end: Some(span.end()),
67                }),
68                Err(e) => Err(e),
69            }
70        }
71        Some(tree) => Err(Error::UnexpectedToken { tree }),
72        None => Err(Error::UnexpectedEndOfInput),
73    }
74}
75
76pub(crate) fn consume_any_ident(
77    idents: &[&str],
78    chars: &mut Peekable<token_stream::IntoIter>,
79) -> Result<Span, Error> {
80    match chars.peek() {
81        Some(TokenTree::Ident(char)) if idents.contains(&char.to_string().as_str()) => {
82            let ret = Ok(char.span());
83            drop(chars.next());
84            ret
85        }
86        Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
87        None => Err(Error::UnexpectedEndOfInput),
88    }
89}
90
91pub(crate) fn consume_punct(
92    c: char,
93    chars: &mut Peekable<token_stream::IntoIter>,
94) -> Result<Span, Error> {
95    match chars.peek() {
96        Some(TokenTree::Punct(punct)) if *punct == c => {
97            let ret = Ok(punct.span());
98            drop(chars.next());
99            ret
100        }
101        Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
102        None => Err(Error::UnexpectedEndOfInput),
103    }
104}
105
106fn jan_weekday(year: i32, ordinal: i32) -> u8 {
107    macro_rules! div_floor {
108        ($a:expr, $b:expr) => {{
109            let (_quotient, _remainder) = ($a / $b, $a % $b);
110            if (_remainder > 0 && $b < 0) || (_remainder < 0 && $b > 0) {
111                _quotient - 1
112            } else {
113                _quotient
114            }
115        }};
116    }
117
118    let adj_year = year - 1;
119    (ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
120        + div_floor!(adj_year, 400)
121        + 6)
122    .rem_euclid(7)
123    .cast_unsigned()
124    .truncate()
125}
126
127pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
128    [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month.widen::<usize>() - 1]
129        + u8::from(month == 2 && is_leap_year(year))
130}
131
132pub(crate) fn ywd_to_yo(
133    year: i32,
134    week: u8,
135    iso_weekday_number: u8,
136    iso_weekday_span: Span,
137) -> Result<(i32, u16), Error> {
138    let (ordinal, overflow) = (u16::from(week) * 7 + u16::from(iso_weekday_number))
139        .overflowing_sub(u16::from(jan_weekday(year, 4)) + 4);
140
141    if overflow || ordinal == 0 {
142        return Ok((year - 1, (ordinal.wrapping_add(days_in_year(year - 1)))));
143    }
144
145    let days_in_cur_year = days_in_year(year);
146    if ordinal > days_in_cur_year {
147        if year == MAX_YEAR {
148            return Err(Error::InvalidComponent {
149                name: "weekday",
150                value: iso_weekday_number.to_string(),
151                span_start: Some(iso_weekday_span),
152                span_end: Some(iso_weekday_span),
153            });
154        }
155        Ok((year + 1, ordinal - days_in_cur_year))
156    } else {
157        Ok((year, ordinal))
158    }
159}
160
161pub(crate) fn ymd_to_yo(year: i32, month: u8, day: u8) -> (i32, u16) {
162    let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
163        [month.widen::<usize>() - 1]
164        + u16::from(month > 2 && is_leap_year(year));
165
166    (year, ordinal + u16::from(day))
167}