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}