questdb/ingress/
timestamp.rs1use crate::error;
2use std::time::{Duration, SystemTime, UNIX_EPOCH};
3
4#[cfg(feature = "chrono_timestamp")]
5use chrono::{DateTime, TimeZone};
6
7#[inline]
10fn sys_time_to_duration(time: SystemTime, extract_fn: impl FnOnce(Duration) -> u128) -> i128 {
11 if time >= UNIX_EPOCH {
12 extract_fn(time.duration_since(UNIX_EPOCH).expect("time >= UNIX_EPOCH")) as i128
13 } else {
14 -(extract_fn(UNIX_EPOCH.duration_since(time).expect("time < UNIX_EPOCH")) as i128)
15 }
16}
17
18#[inline]
19fn sys_time_convert(
20 time: SystemTime,
21 extract_fn: impl FnOnce(Duration) -> u128,
22) -> crate::Result<i64> {
23 let number = sys_time_to_duration(time, extract_fn);
24 match i64::try_from(number) {
25 Ok(number) => Ok(number),
26 Err(_) => Err(error::fmt!(
27 InvalidTimestamp,
28 "Timestamp {:?} is out of range",
29 time
30 )),
31 }
32}
33
34#[inline]
35fn extract_current_timestamp(extract_fn: impl FnOnce(Duration) -> u128) -> crate::Result<i64> {
36 let time = SystemTime::now();
37 sys_time_convert(time, extract_fn)
38}
39
40#[derive(Copy, Clone, Debug)]
91pub struct TimestampMicros(i64);
92
93impl TimestampMicros {
94 pub fn now() -> Self {
96 Self(extract_current_timestamp(|d| d.as_micros()).expect("now in range of micros"))
97 }
98
99 pub fn new(micros: i64) -> Self {
102 Self(micros)
103 }
104
105 #[cfg(feature = "chrono_timestamp")]
106 pub fn from_datetime<T: TimeZone>(dt: DateTime<T>) -> Self {
107 Self::new(dt.timestamp_micros())
108 }
109
110 pub fn from_systemtime(time: SystemTime) -> crate::Result<Self> {
111 sys_time_convert(time, |d| d.as_micros()).map(Self)
112 }
113
114 pub fn as_i64(&self) -> i64 {
116 self.0
117 }
118}
119
120#[derive(Copy, Clone, Debug)]
172pub struct TimestampNanos(i64);
173
174impl TimestampNanos {
175 pub fn now() -> Self {
177 Self(extract_current_timestamp(|d| d.as_nanos()).expect("now in range of nanos"))
178 }
179
180 pub fn new(nanos: i64) -> Self {
183 Self(nanos)
184 }
185
186 #[cfg(feature = "chrono_timestamp")]
187 pub fn from_datetime<T: TimeZone>(dt: DateTime<T>) -> crate::Result<Self> {
188 match dt.timestamp_nanos_opt() {
189 Some(nanos) => Ok(Self::new(nanos)),
190 None => Err(error::fmt!(
191 InvalidTimestamp,
192 "Timestamp {:?} is out of range",
193 dt
194 )),
195 }
196 }
197
198 pub fn from_systemtime(time: SystemTime) -> crate::Result<Self> {
199 sys_time_convert(time, |d| d.as_nanos()).map(Self)
200 }
201
202 pub fn as_i64(&self) -> i64 {
204 self.0
205 }
206}
207
208impl TryFrom<TimestampMicros> for TimestampNanos {
209 type Error = crate::Error;
210
211 fn try_from(ts: TimestampMicros) -> crate::Result<Self> {
212 let nanos = ts.as_i64().checked_mul(1000i64);
213 match nanos {
214 Some(nanos) => Ok(Self(nanos)),
215 None => Err(error::fmt!(
216 InvalidTimestamp,
217 "Timestamp {:?} is out of range",
218 ts
219 )),
220 }
221 }
222}
223
224impl From<TimestampNanos> for TimestampMicros {
225 fn from(ts: TimestampNanos) -> Self {
226 Self(ts.as_i64() / 1000i64)
227 }
228}
229
230#[derive(Copy, Clone, Debug)]
240pub enum Timestamp {
241 Micros(TimestampMicros),
242 Nanos(TimestampNanos),
243}
244
245impl From<TimestampMicros> for Timestamp {
246 fn from(ts: TimestampMicros) -> Self {
247 Self::Micros(ts)
248 }
249}
250
251impl From<TimestampNanos> for Timestamp {
252 fn from(ts: TimestampNanos) -> Self {
253 Self::Nanos(ts)
254 }
255}
256
257impl TryFrom<Timestamp> for TimestampMicros {
258 type Error = crate::Error;
259
260 fn try_from(ts: Timestamp) -> crate::Result<Self> {
261 match ts {
262 Timestamp::Micros(ts) => Ok(ts),
263 Timestamp::Nanos(ts) => Ok(ts.into()),
264 }
265 }
266}
267
268impl TryFrom<Timestamp> for TimestampNanos {
269 type Error = crate::Error;
270
271 fn try_from(ts: Timestamp) -> crate::Result<Self> {
272 match ts {
273 Timestamp::Micros(ts) => Ok(ts.try_into()?),
274 Timestamp::Nanos(ts) => Ok(ts),
275 }
276 }
277}