timestamp.rs (7641B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024, 2025, 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 use std::{fmt::Display, ops::Add, str::FromStr, time::Duration}; 18 19 use jiff::{SignedDuration, Timestamp, civil::Time, tz::TimeZone}; 20 use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeStruct}; // codespell:ignore 21 use serde_json::Value; 22 23 /// <https://docs.taler.net/core/api-common.html#tsref-type-Timestamp> 24 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 25 pub enum TalerTimestamp { 26 Never, 27 Timestamp(Timestamp), 28 } 29 30 impl TalerTimestamp { 31 /** Encode timestamp for signature */ 32 pub fn signature_bytes(self) -> [u8; 8] { 33 match self { 34 TalerTimestamp::Never => u64::MAX, 35 // Truncate to second and then encode into microseconds as JSON format only support second precision 36 TalerTimestamp::Timestamp(timestamp) => (timestamp.as_second() as u64) * 1000 * 1000, 37 } 38 .to_be_bytes() 39 } 40 41 /// Returns an absolute duration representing the elapsed time from this timestamp until the given other timestamp. 42 pub fn duration_until(self, other: Timestamp) -> SignedDuration { 43 match self { 44 TalerTimestamp::Never => SignedDuration::MAX, 45 TalerTimestamp::Timestamp(tm) => tm.duration_until(other), 46 } 47 } 48 } 49 50 impl FromStr for TalerTimestamp { 51 type Err = anyhow::Error; 52 53 fn from_str(s: &str) -> Result<Self, Self::Err> { 54 if s == "never" { 55 return Ok(Self::Never); 56 } 57 let s: i64 = s.parse()?; 58 59 Ok(Self::Timestamp(jiff::Timestamp::from_second(s)?)) 60 } 61 } 62 63 #[derive(Serialize, Deserialize)] 64 struct TimestampImpl { 65 t_s: Value, 66 } 67 68 impl<'de> Deserialize<'de> for TalerTimestamp { 69 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 70 where 71 D: Deserializer<'de>, 72 { 73 let tmp = TimestampImpl::deserialize(deserializer)?; 74 match tmp.t_s { 75 Value::Number(s) => { 76 if let Some(since_epoch_s) = s.as_u64() { 77 jiff::Timestamp::from_second(since_epoch_s as i64) 78 .map(Self::Timestamp) 79 .map_err(Error::custom) 80 } else { 81 Err(Error::custom("Expected epoch time")) 82 } 83 } 84 Value::String(str) if str == "never" => Ok(Self::Never), 85 _ => Err(Error::custom("Expected epoch time or 'never'")), 86 } 87 } 88 } 89 90 impl Serialize for TalerTimestamp { 91 fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error> 92 where 93 S: Serializer, 94 { 95 let mut se_struct = se.serialize_struct("Timestamp", 1)?; 96 match self { 97 TalerTimestamp::Never => se_struct.serialize_field("t_s", "never")?, 98 TalerTimestamp::Timestamp(timestamp) => { 99 se_struct.serialize_field("t_s", ×tamp.as_second())? 100 } 101 } 102 se_struct.end() 103 } 104 } 105 106 impl Display for TalerTimestamp { 107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 108 match self { 109 TalerTimestamp::Never => f.write_str("never"), 110 TalerTimestamp::Timestamp(timestamp) => timestamp.fmt(f), 111 } 112 } 113 } 114 115 impl From<jiff::Timestamp> for TalerTimestamp { 116 fn from(time: jiff::Timestamp) -> Self { 117 Self::Timestamp(time) 118 } 119 } 120 121 impl From<jiff::civil::Date> for TalerTimestamp { 122 fn from(date: jiff::civil::Date) -> Self { 123 date.to_datetime(Time::midnight()) 124 .to_zoned(TimeZone::UTC) 125 .unwrap() 126 .timestamp() 127 .into() 128 } 129 } 130 131 impl Add<jiff::Span> for TalerTimestamp { 132 type Output = Self; 133 134 fn add(self, rhs: jiff::Span) -> Self::Output { 135 match self { 136 TalerTimestamp::Never => TalerTimestamp::Never, 137 TalerTimestamp::Timestamp(timestamp) => TalerTimestamp::Timestamp(timestamp + rhs), 138 } 139 } 140 } 141 142 impl sqlx::Type<sqlx::Postgres> for TalerTimestamp { 143 fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo { 144 Option::<i64>::type_info() 145 } 146 } 147 148 impl<'q> sqlx::Encode<'q, sqlx::Postgres> for TalerTimestamp { 149 fn encode_by_ref( 150 &self, 151 buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>, 152 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> { 153 match self { 154 TalerTimestamp::Never => None, 155 TalerTimestamp::Timestamp(timestamp) => Some(timestamp.as_microsecond()), 156 } 157 .encode_by_ref(buf) 158 } 159 } 160 161 impl<'r> sqlx::Decode<'r, sqlx::Postgres> for TalerTimestamp { 162 fn decode( 163 value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>, 164 ) -> Result<Self, sqlx::error::BoxDynError> { 165 let micros = Option::<i64>::decode(value)?; 166 Ok(match micros { 167 Some(micros) => Self::Timestamp(Timestamp::from_microsecond(micros)?), 168 None => Self::Never, 169 }) 170 } 171 } 172 173 /// <https://docs.taler.net/core/api-common.html#tsref-type-RelativeTime> 174 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 175 pub enum RelativeTime { 176 Forever, 177 Duration(Duration), 178 } 179 180 impl FromStr for RelativeTime { 181 type Err = anyhow::Error; 182 183 fn from_str(s: &str) -> Result<Self, Self::Err> { 184 if s == "forever" { 185 return Ok(Self::Forever); 186 } 187 let micros: u64 = s.parse()?; 188 189 Ok(Self::Duration(Duration::from_micros(micros))) 190 } 191 } 192 193 #[derive(Serialize, Deserialize)] 194 struct RelativeTimeImpl { 195 d_us: Value, 196 } 197 198 impl<'de> Deserialize<'de> for RelativeTime { 199 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 200 where 201 D: Deserializer<'de>, 202 { 203 let tmp = RelativeTimeImpl::deserialize(deserializer)?; 204 match tmp.d_us { 205 Value::Number(s) => { 206 if let Some(micros) = s.as_u64() { 207 Ok(Self::Duration(Duration::from_micros(micros))) 208 } else { 209 Err(Error::custom("Expected microseconds")) 210 } 211 } 212 Value::String(str) if str == "forever" => Ok(Self::Forever), 213 _ => Err(Error::custom("Expected epoch time or 'forever'")), 214 } 215 } 216 } 217 218 impl Serialize for RelativeTime { 219 fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error> 220 where 221 S: Serializer, 222 { 223 let mut se_struct = se.serialize_struct("RelativeTime", 1)?; 224 match self { 225 RelativeTime::Forever => se_struct.serialize_field("d_us", "forever")?, 226 RelativeTime::Duration(duration) => { 227 se_struct.serialize_field("d_us", &duration.as_micros())? 228 } 229 } 230 se_struct.end() 231 } 232 } 233 234 impl Display for RelativeTime { 235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 236 match self { 237 RelativeTime::Forever => f.write_str("forever"), 238 RelativeTime::Duration(duration) => write!(f, "{duration:?}"), 239 } 240 } 241 }