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 {
44 seconds: i64,
45 nanoseconds: u32,
46}
47
48pub(crate) fn parse(tokens: &mut Peekable<token_stream::IntoIter>) -> Result<Timestamp, Error> {
49 let mut tokens2 = tokens.clone();
53 if let Ok(udt) = utc_datetime::parse(&mut tokens2) {
54 *tokens = tokens2;
55 let (seconds, nanoseconds) = udt.to_timestamp();
56 return Ok(Timestamp {
57 seconds,
58 nanoseconds,
59 });
60 }
61
62 let (sign_span, is_negative) = match consume_punct('-', tokens) {
63 Ok(span) => (Some(span), true),
64 Err(_) => (None, false),
65 };
66 let (span, raw) = match tokens.next() {
67 Some(TokenTree::Literal(literal)) => (literal.span(), literal.to_string()),
68 Some(tree) => return Err(Error::UnexpectedToken { tree }),
69 None => return Err(Error::UnexpectedEndOfInput),
70 };
71 let (seconds_str, fractional) = match raw.split_once('.') {
72 Some((int, frac)) => (int, Some(frac)),
73 None => (raw.as_str(), None),
74 };
75
76 let seconds = parse_number::<i64>("timestamp", seconds_str)?;
77 let nanoseconds = if let Some(fractional) = fractional {
78 let nanoseconds = fractional
79 .replace('_', "")
80 .chars()
81 .chain(iter::repeat('0'))
82 .take(9)
83 .collect::<String>();
84 parse_number::<u32>("timestamp", &nanoseconds)?
85 } else {
86 0
87 };
88
89 let (seconds, nanoseconds) = match (is_negative, nanoseconds != 0) {
90 (true, true) => (-seconds - 1, 1_000_000_000 - nanoseconds),
91 (true, false) => (-seconds, 0),
92 (false, _) => (seconds, nanoseconds),
93 };
94
95 if seconds > MAX {
96 return Err(Error::Custom {
97 message: "timestamp is too large".into(),
98 span_start: sign_span.or(Some(span)),
99 span_end: Some(span),
100 });
101 }
102 if seconds < MIN {
103 return Err(Error::Custom {
104 message: "timestamp is too small".into(),
105 span_start: sign_span.or(Some(span)),
106 span_end: Some(span),
107 });
108 }
109
110 Ok(Timestamp {
111 seconds,
112 nanoseconds,
113 })
114}
115
116impl ToTokenStream for Timestamp {
117 fn append_to(self, ts: &mut proc_macro::TokenStream) {
118 quote_append! { ts
119 unsafe {
120 ::time::Timestamp::__new_unchecked(
121 #(self.seconds),
122 #(self.nanoseconds)
123 )
124 }
125 }
126 }
127}