ssi_jwt/datatype/
numeric_date.rs1use std::str::FromStr;
2
3use chrono::{prelude::*, Duration, LocalResult};
4use ordered_float::NotNan;
5use serde::{Deserialize, Serialize, Serializer};
6
7#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct NumericDate(#[serde(serialize_with = "interop_serialize")] NotNan<f64>);
14
15fn interop_serialize<S>(x: &f64, s: S) -> Result<S::Ok, S::Error>
19where
20 S: Serializer,
21{
22 if x.fract() != 0.0 {
23 s.serialize_f64(*x)
24 } else {
25 s.serialize_i64(*x as i64)
26 }
27}
28
29#[derive(thiserror::Error, Debug)]
30pub enum NumericDateConversionError {
31 #[error("Not a number")]
32 Nan,
33
34 #[error("Invalid float literal")]
35 InvalidFloatLiteral,
36}
37
38impl From<ordered_float::FloatIsNan> for NumericDateConversionError {
39 fn from(_: ordered_float::FloatIsNan) -> Self {
40 Self::Nan
41 }
42}
43
44impl NumericDate {
45 pub fn as_seconds(self) -> f64 {
47 *self.0
48 }
49
50 pub fn try_from_seconds(seconds: f64) -> Result<Self, NumericDateConversionError> {
52 let seconds = NotNan::new(seconds)?;
53 Ok(NumericDate(seconds))
54 }
55
56 fn into_whole_seconds_and_fractional_nanoseconds(self) -> (i64, u32) {
58 let whole_seconds = self.0.floor() as i64;
59 let fractional_nanoseconds = ((self.0 - self.0.floor()) * 1_000_000_000.0).floor() as u32;
60 assert!(fractional_nanoseconds < 1_000_000_000);
61 (whole_seconds, fractional_nanoseconds)
62 }
63}
64
65impl std::ops::Add<Duration> for NumericDate {
67 type Output = NumericDate;
68 fn add(self, rhs: Duration) -> Self::Output {
69 let self_dtu: DateTime<Utc> = self.into();
70 Self::Output::from(self_dtu + rhs)
71 }
72}
73
74impl std::ops::Sub<NumericDate> for NumericDate {
76 type Output = Duration;
77 fn sub(self, rhs: NumericDate) -> Self::Output {
78 let self_dtu: DateTime<Utc> = self.into();
79 let rhs_dtu: DateTime<Utc> = rhs.into();
80 self_dtu - rhs_dtu
81 }
82}
83
84impl std::ops::Sub<Duration> for NumericDate {
86 type Output = NumericDate;
87 fn sub(self, rhs: Duration) -> Self::Output {
88 let self_dtu: DateTime<Utc> = self.into();
89 Self::Output::from(self_dtu - rhs)
90 }
91}
92
93impl From<i32> for NumericDate {
94 fn from(value: i32) -> Self {
95 Self(NotNan::new(value as f64).unwrap())
96 }
97}
98
99impl TryFrom<i64> for NumericDate {
100 type Error = NumericDateConversionError;
101
102 fn try_from(value: i64) -> Result<Self, Self::Error> {
103 Self::try_from_seconds(value as f64)
104 }
105}
106
107impl TryFrom<f64> for NumericDate {
108 type Error = NumericDateConversionError;
109
110 fn try_from(value: f64) -> Result<Self, Self::Error> {
111 Self::try_from_seconds(value)
112 }
113}
114
115impl From<DateTime<Utc>> for NumericDate {
116 fn from(dtu: DateTime<Utc>) -> Self {
117 let whole_seconds = dtu.timestamp() as f64;
120 let fractional_seconds = match dtu.timestamp_nanos_opt() {
121 Some(nanos) => nanos.rem_euclid(1_000_000_000) as f64 * 1.0e-9,
122 None => dtu.timestamp_micros().rem_euclid(1_000_000) as f64 * 1.0e-6,
123 };
124
125 Self::try_from_seconds(whole_seconds + fractional_seconds)
126 .unwrap()
128 }
129}
130
131impl From<DateTime<FixedOffset>> for NumericDate {
132 fn from(dtfo: DateTime<FixedOffset>) -> Self {
133 DateTime::<Utc>::from(dtfo).into()
134 }
135}
136
137impl From<NumericDate> for DateTime<Utc> {
138 fn from(nd: NumericDate) -> Self {
139 let (whole_seconds, fractional_nanoseconds) =
140 nd.into_whole_seconds_and_fractional_nanoseconds();
141 Utc.timestamp_opt(whole_seconds, fractional_nanoseconds)
143 .unwrap()
144 }
145}
146
147impl From<NumericDate> for LocalResult<DateTime<Utc>> {
148 fn from(nd: NumericDate) -> Self {
149 let (whole_seconds, fractional_nanoseconds) =
150 nd.into_whole_seconds_and_fractional_nanoseconds();
151 Utc.timestamp_opt(whole_seconds, fractional_nanoseconds)
152 }
153}
154
155impl FromStr for NumericDate {
156 type Err = NumericDateConversionError;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 let f: NotNan<f64> = s
160 .parse()
161 .map_err(|_| NumericDateConversionError::InvalidFloatLiteral)?;
162 Ok(Self(f))
163 }
164}