1use std::iter::Peekable;
2
3use proc_macro::{TokenTree, token_stream};
4use time_core::unit::*;
5use time_core::util::days_in_year;
6
7use crate::date::{Date, MAX_YEAR, MIN_YEAR};
8use crate::helpers::{consume_punct, parse_number};
9use crate::time::Time;
10use crate::to_tokens::ToTokenStream;
11use crate::utc_datetime::UtcDateTime;
12use crate::{Error, utc_datetime};
13
14const MAX: i64 = UtcDateTime {
15 date: Date {
16 year: MAX_YEAR,
17 ordinal: days_in_year(MAX_YEAR),
18 },
19 time: Time {
20 hour: 23,
21 minute: 59,
22 second: 59,
23 nanosecond: 999_999_999,
24 },
25}
26.to_timestamp()
27.0;
28
29const MIN: i64 = UtcDateTime {
30 date: Date {
31 year: MIN_YEAR,
32 ordinal: 1,
33 },
34 time: Time {
35 hour: 0,
36 minute: 0,
37 second: 0,
38 nanosecond: 0,
39 },
40}
41.to_timestamp()
42.0;
43
44pub(crate) struct Timestamp {
45 seconds: i64,
46 nanoseconds: u32,
47}
48
49pub(crate) fn parse(tokens: &mut Peekable<token_stream::IntoIter>) -> Result<Timestamp, Error> {
50 let mut tokens2 = tokens.clone();
54 if let Ok(udt) = utc_datetime::parse(&mut tokens2) {
55 *tokens = tokens2;
56 let (seconds, nanoseconds) = udt.to_timestamp();
57 return Ok(Timestamp {
58 seconds,
59 nanoseconds,
60 });
61 }
62
63 let (sign_span, is_negative) = match consume_punct('-', tokens) {
64 Ok(span) => (Some(span), true),
65 Err(_) => (None, false),
66 };
67 let (span, raw) = match tokens.next() {
68 Some(TokenTree::Literal(literal)) => (literal.span(), literal.to_string()),
69 Some(tree) => return Err(Error::UnexpectedToken { tree }),
70 None => return Err(Error::UnexpectedEndOfInput),
71 };
72 let (seconds_str, fractional) = match raw.split_once('.') {
73 Some((int, frac)) => (int, Some(frac)),
74 None => (raw.as_str(), None),
75 };
76
77 let seconds = parse_number::<i64>("timestamp", seconds_str)?;
78 let nanoseconds = if let Some(fractional) = fractional {
79 let frac = format!("0.{fractional}");
83 let parsed = parse_number::<f64>("timestamp", &frac)?;
84 (parsed.fract() * Nanosecond::per_t::<f64>(Second)).round() as u32
85 } else {
86 0
87 };
88
89 let (seconds, nanoseconds) = match (is_negative, fractional.is_some()) {
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}