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