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