1use std::iter::{self, Peekable};
2
3use proc_macro::{TokenTree, token_stream};
4use time_core::util::days_in_year;
5
6use crate::date::{Date, MAX_YEAR, MIN_YEAR};
7use crate::helpers::{consume_punct, parse_number};
8use crate::time::Time;
9use crate::to_tokens::ToTokenStream;
10use crate::utc_datetime::UtcDateTime;
11use crate::{Error, utc_datetime};
12
13const MAX: i64 = UtcDateTime {
14 date: Date {
15 year: MAX_YEAR,
16 ordinal: days_in_year(MAX_YEAR),
17 },
18 time: Time {
19 hour: 23,
20 minute: 59,
21 second: 59,
22 nanosecond: 999_999_999,
23 },
24}
25.to_timestamp()
26.0;
27
28const MIN: i64 = UtcDateTime {
29 date: Date {
30 year: MIN_YEAR,
31 ordinal: 1,
32 },
33 time: Time {
34 hour: 0,
35 minute: 0,
36 second: 0,
37 nanosecond: 0,
38 },
39}
40.to_timestamp()
41.0;
42
43pub(crate) struct Timestamp {
48 seconds: i64,
49 nanoseconds: u32,
50}
51
52pub(crate) fn parse(tokens: &mut Peekable<token_stream::IntoIter>) -> Result<Timestamp, Error> {
53 let mut tokens2 = tokens.clone();
57 if let Ok(udt) = utc_datetime::parse(&mut tokens2) {
58 *tokens = tokens2;
59 let (seconds, nanoseconds) = udt.to_timestamp();
60 return Ok(Timestamp {
61 seconds,
62 nanoseconds,
63 });
64 }
65
66 let (sign_span, is_negative) = match consume_punct('-', tokens) {
67 Ok(span) => (Some(span), true),
68 Err(_) => (None, false),
69 };
70 let (span, raw) = match tokens.next() {
71 Some(TokenTree::Literal(literal)) => (literal.span(), literal.to_string()),
72 Some(tree) => return Err(Error::UnexpectedToken { tree }),
73 None => return Err(Error::UnexpectedEndOfInput),
74 };
75 let (seconds_str, fractional) = match raw.split_once('.') {
76 Some((int, frac)) => (int, Some(frac)),
77 None => (raw.as_str(), None),
78 };
79
80 let seconds = parse_number::<i64>("timestamp", seconds_str)?;
81 let nanoseconds = if let Some(fractional) = fractional {
82 let nanoseconds = fractional
83 .replace('_', "")
84 .chars()
85 .chain(iter::repeat('0'))
86 .take(9)
87 .collect::<String>();
88 parse_number::<u32>("timestamp", &nanoseconds)?
89 } else {
90 0
91 };
92
93 let (seconds, nanoseconds) = match (is_negative, nanoseconds != 0) {
94 (true, true) => (-seconds - 1, 1_000_000_000 - nanoseconds),
95 (true, false) => (-seconds, 0),
96 (false, _) => (seconds, nanoseconds),
97 };
98
99 if seconds > MAX {
100 return Err(Error::Custom {
101 message: "timestamp is too large".into(),
102 span_start: sign_span.or(Some(span)),
103 span_end: Some(span),
104 });
105 }
106 if seconds < MIN {
107 return Err(Error::Custom {
108 message: "timestamp is too small".into(),
109 span_start: sign_span.or(Some(span)),
110 span_end: Some(span),
111 });
112 }
113
114 Ok(Timestamp {
115 seconds,
116 nanoseconds,
117 })
118}
119
120impl ToTokenStream for Timestamp {
121 fn into_token_stream(self) -> proc_macro::TokenStream {
122 quote! {
125 unsafe {
126 ::time::Timestamp::__new_unchecked(
127 #(self.seconds),
128 #(self.nanoseconds)
129 )
130 }
131 }
132 }
133}