1use std::{num::ParseIntError, str::FromStr};
2
3use anyhow::Result;
4
5cfg_if::cfg_if! {
6 if #[cfg(feature = "clock")] {
7 use chrono::{DateTime, TimeZone, Utc};
8 use anyhow::anyhow;
9 }
10}
11use serde::{Deserialize, Serialize};
12#[cfg(feature = "ts-bindings")]
13use ts_rs::TS;
14use utoipa::ToSchema;
15
16#[derive(
17 Serialize,
18 Deserialize,
19 Clone,
20 Copy,
21 Debug,
22 PartialEq,
23 Eq,
24 Hash,
25 bincode::Encode,
26 bincode::Decode,
27 ToSchema,
28)]
29pub struct Duration {
30 pub secs: u64,
31}
32
33impl From<Duration> for std::time::Duration {
34 fn from(d: Duration) -> Self {
35 std::time::Duration::from_secs(d.secs)
36 }
37}
38
39#[repr(transparent)]
40#[cfg_attr(feature = "ts-bindings", derive(TS))]
41#[derive(
42 Debug, Hash, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ToSchema,
43)]
44pub struct Timestamp(u64);
45
46impl Timestamp {
47 pub fn from_nanos(nanos: u64) -> Self {
49 Timestamp(nanos)
50 }
51
52 #[cfg(feature = "clock")]
54 pub fn from_datetime(dt: DateTime<Utc>) -> Result<Self> {
55 let nanos = dt
56 .timestamp_nanos_opt()
57 .ok_or_else(|| anyhow!("Invalid timestamp"))?;
58
59 if nanos < 0 {
60 return Err(anyhow!("Timestamp cannot represent dates before 1970"));
61 }
62
63 Ok(Timestamp(nanos as u64))
64 }
65
66 #[cfg(feature = "clock")]
67 pub fn into_datetime(self) -> DateTime<Utc> {
68 Utc.timestamp_nanos(self.0 as i64)
69 }
70
71 pub fn as_nanos(&self) -> u64 {
73 self.0
74 }
75
76 #[cfg(feature = "clock")]
78 pub fn now() -> Self {
79 Self::from_datetime(Utc::now()).expect("Current time should always be valid")
81 }
82}
83
84impl FromStr for Timestamp {
86 type Err = ParseIntError;
87
88 fn from_str(s: &str) -> Result<Self, Self::Err> {
89 let nanos: u64 = s.parse()?;
90 Ok(Timestamp::from_nanos(nanos))
91 }
92}