diff options
Diffstat (limited to 'vendor/time-macros/src/date.rs')
-rw-r--r-- | vendor/time-macros/src/date.rs | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/vendor/time-macros/src/date.rs b/vendor/time-macros/src/date.rs new file mode 100644 index 000000000..574ef8ce6 --- /dev/null +++ b/vendor/time-macros/src/date.rs @@ -0,0 +1,137 @@ +use std::iter::Peekable; + +use proc_macro::{token_stream, TokenTree}; +use time_core::util::{days_in_year, weeks_in_year}; + +use crate::helpers::{ + consume_any_ident, consume_number, consume_punct, days_in_year_month, ymd_to_yo, ywd_to_yo, +}; +use crate::to_tokens::ToTokenTree; +use crate::Error; + +#[cfg(feature = "large-dates")] +const MAX_YEAR: i32 = 999_999; +#[cfg(not(feature = "large-dates"))] +const MAX_YEAR: i32 = 9_999; + +pub(crate) struct Date { + pub(crate) year: i32, + pub(crate) ordinal: u16, +} + +pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date, Error> { + let (year_sign_span, year_sign, explicit_sign) = if let Ok(span) = consume_punct('-', chars) { + (Some(span), -1, true) + } else if let Ok(span) = consume_punct('+', chars) { + (Some(span), 1, true) + } else { + (None, 1, false) + }; + let (year_span, mut year) = consume_number::<i32>("year", chars)?; + year *= year_sign; + if year.abs() > MAX_YEAR { + return Err(Error::InvalidComponent { + name: "year", + value: year.to_string(), + span_start: Some(year_sign_span.unwrap_or(year_span)), + span_end: Some(year_span), + }); + } + if !explicit_sign && year.abs() >= 10_000 { + return Err(Error::Custom { + message: "years with more than four digits must have an explicit sign".into(), + span_start: Some(year_sign_span.unwrap_or(year_span)), + span_end: Some(year_span), + }); + } + + consume_punct('-', chars)?; + + // year-week-day + if let Ok(w_span) = consume_any_ident(&["W"], chars) { + let (week_span, week) = consume_number::<u8>("week", chars)?; + consume_punct('-', chars)?; + let (day_span, day) = consume_number::<u8>("day", chars)?; + + if week > weeks_in_year(year) { + return Err(Error::InvalidComponent { + name: "week", + value: week.to_string(), + span_start: Some(w_span), + span_end: Some(week_span), + }); + } + if day == 0 || day > 7 { + return Err(Error::InvalidComponent { + name: "day", + value: day.to_string(), + span_start: Some(day_span), + span_end: Some(day_span), + }); + } + + let (year, ordinal) = ywd_to_yo(year, week, day); + + return Ok(Date { year, ordinal }); + } + + // We don't yet know whether it's year-month-day or year-ordinal. + let (month_or_ordinal_span, month_or_ordinal) = + consume_number::<u16>("month or ordinal", chars)?; + + // year-month-day + #[allow(clippy::branches_sharing_code)] // clarity + if consume_punct('-', chars).is_ok() { + let (month_span, month) = (month_or_ordinal_span, month_or_ordinal); + let (day_span, day) = consume_number::<u8>("day", chars)?; + + if month == 0 || month > 12 { + return Err(Error::InvalidComponent { + name: "month", + value: month.to_string(), + span_start: Some(month_span), + span_end: Some(month_span), + }); + } + let month = month as _; + if day == 0 || day > days_in_year_month(year, month) { + return Err(Error::InvalidComponent { + name: "day", + value: day.to_string(), + span_start: Some(day_span), + span_end: Some(day_span), + }); + } + + let (year, ordinal) = ymd_to_yo(year, month, day); + + Ok(Date { year, ordinal }) + } + // year-ordinal + else { + let (ordinal_span, ordinal) = (month_or_ordinal_span, month_or_ordinal); + + if ordinal == 0 || ordinal > days_in_year(year) { + return Err(Error::InvalidComponent { + name: "ordinal", + value: ordinal.to_string(), + span_start: Some(ordinal_span), + span_end: Some(ordinal_span), + }); + } + + Ok(Date { year, ordinal }) + } +} + +impl ToTokenTree for Date { + fn into_token_tree(self) -> TokenTree { + quote_group! {{ + const DATE: ::time::Date = ::time::Date::__from_ordinal_date_unchecked( + #(self.year), + #(self.ordinal), + ); + DATE + }} + } +} |