1 use std
::iter
::Peekable
;
3 use proc_macro
::{token_stream, TokenTree}
;
4 use time_core
::util
::{days_in_year, weeks_in_year}
;
7 consume_any_ident
, consume_number
, consume_punct
, days_in_year_month
, ymd_to_yo
, ywd_to_yo
,
9 use crate::to_tokens
::ToTokenTree
;
12 #[cfg(feature = "large-dates")]
13 const MAX_YEAR
: i32 = 999_999;
14 #[cfg(not(feature = "large-dates"))]
15 const MAX_YEAR
: i32 = 9_999;
17 pub(crate) struct Date
{
19 pub(crate) ordinal
: u16,
22 pub(crate) fn parse(chars
: &mut Peekable
<token_stream
::IntoIter
>) -> Result
<Date
, Error
> {
23 let (year_sign_span
, year_sign
, explicit_sign
) = if let Ok(span
) = consume_punct('
-'
, chars
) {
24 (Some(span
), -1, true)
25 } else if let Ok(span
) = consume_punct('
+'
, chars
) {
30 let (year_span
, mut year
) = consume_number
::<i32>("year", chars
)?
;
32 if year
.abs() > MAX_YEAR
{
33 return Err(Error
::InvalidComponent
{
35 value
: year
.to_string(),
36 span_start
: Some(year_sign_span
.unwrap_or(year_span
)),
37 span_end
: Some(year_span
),
40 if !explicit_sign
&& year
.abs() >= 10_000 {
41 return Err(Error
::Custom
{
42 message
: "years with more than four digits must have an explicit sign".into(),
43 span_start
: Some(year_sign_span
.unwrap_or(year_span
)),
44 span_end
: Some(year_span
),
48 consume_punct('
-'
, chars
)?
;
51 if let Ok(w_span
) = consume_any_ident(&["W"], chars
) {
52 let (week_span
, week
) = consume_number
::<u8>("week", chars
)?
;
53 consume_punct('
-'
, chars
)?
;
54 let (day_span
, day
) = consume_number
::<u8>("day", chars
)?
;
56 if week
> weeks_in_year(year
) {
57 return Err(Error
::InvalidComponent
{
59 value
: week
.to_string(),
60 span_start
: Some(w_span
),
61 span_end
: Some(week_span
),
64 if day
== 0 || day
> 7 {
65 return Err(Error
::InvalidComponent
{
67 value
: day
.to_string(),
68 span_start
: Some(day_span
),
69 span_end
: Some(day_span
),
73 let (year
, ordinal
) = ywd_to_yo(year
, week
, day
);
75 return Ok(Date { year, ordinal }
);
78 // We don't yet know whether it's year-month-day or year-ordinal.
79 let (month_or_ordinal_span
, month_or_ordinal
) =
80 consume_number
::<u16>("month or ordinal", chars
)?
;
83 #[allow(clippy::branches_sharing_code)] // clarity
84 if consume_punct('
-'
, chars
).is_ok() {
85 let (month_span
, month
) = (month_or_ordinal_span
, month_or_ordinal
);
86 let (day_span
, day
) = consume_number
::<u8>("day", chars
)?
;
88 if month
== 0 || month
> 12 {
89 return Err(Error
::InvalidComponent
{
91 value
: month
.to_string(),
92 span_start
: Some(month_span
),
93 span_end
: Some(month_span
),
96 let month
= month
as _
;
97 if day
== 0 || day
> days_in_year_month(year
, month
) {
98 return Err(Error
::InvalidComponent
{
100 value
: day
.to_string(),
101 span_start
: Some(day_span
),
102 span_end
: Some(day_span
),
106 let (year
, ordinal
) = ymd_to_yo(year
, month
, day
);
108 Ok(Date { year, ordinal }
)
112 let (ordinal_span
, ordinal
) = (month_or_ordinal_span
, month_or_ordinal
);
114 if ordinal
== 0 || ordinal
> days_in_year(year
) {
115 return Err(Error
::InvalidComponent
{
117 value
: ordinal
.to_string(),
118 span_start
: Some(ordinal_span
),
119 span_end
: Some(ordinal_span
),
123 Ok(Date { year, ordinal }
)
127 impl ToTokenTree
for Date
{
128 fn into_token_tree(self) -> TokenTree
{
130 const DATE
: ::time
::Date
= unsafe {
131 ::time
::Date
::__from_ordinal_date_unchecked(