time.rs (7720B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024-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 let seconds = i64::try_from(since_epoch_s).map_err(Error::custom)?; 78 jiff::Timestamp::from_second(seconds) 79 .map(Self::Timestamp) 80 .map_err(Error::custom) 81 } else { 82 Err(Error::custom("Expected epoch time")) 83 } 84 } 85 Value::String(str) if str == "never" => Ok(Self::Never), 86 _ => Err(Error::custom("Expected epoch time or 'never'")), 87 } 88 } 89 } 90 91 impl Serialize for TalerTimestamp { 92 fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error> 93 where 94 S: Serializer, 95 { 96 let mut se_struct = se.serialize_struct("Timestamp", 1)?; 97 match self { 98 TalerTimestamp::Never => se_struct.serialize_field("t_s", "never")?, 99 TalerTimestamp::Timestamp(timestamp) => { 100 se_struct.serialize_field("t_s", ×tamp.as_second())? 101 } 102 } 103 se_struct.end() 104 } 105 } 106 107 impl Display for TalerTimestamp { 108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 109 match self { 110 TalerTimestamp::Never => f.write_str("never"), 111 TalerTimestamp::Timestamp(timestamp) => timestamp.fmt(f), 112 } 113 } 114 } 115 116 impl From<jiff::Timestamp> for TalerTimestamp { 117 fn from(time: jiff::Timestamp) -> Self { 118 Self::Timestamp(time) 119 } 120 } 121 122 impl From<jiff::civil::Date> for TalerTimestamp { 123 fn from(date: jiff::civil::Date) -> Self { 124 date.to_datetime(Time::midnight()) 125 .to_zoned(TimeZone::UTC) 126 .unwrap() 127 .timestamp() 128 .into() 129 } 130 } 131 132 impl Add<jiff::Span> for TalerTimestamp { 133 type Output = Self; 134 135 fn add(self, rhs: jiff::Span) -> Self::Output { 136 match self { 137 TalerTimestamp::Never => TalerTimestamp::Never, 138 TalerTimestamp::Timestamp(timestamp) => TalerTimestamp::Timestamp(timestamp + rhs), 139 } 140 } 141 } 142 143 impl sqlx::Type<sqlx::Postgres> for TalerTimestamp { 144 fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo { 145 Option::<i64>::type_info() 146 } 147 } 148 149 impl<'q> sqlx::Encode<'q, sqlx::Postgres> for TalerTimestamp { 150 fn encode_by_ref( 151 &self, 152 buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>, 153 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> { 154 match self { 155 TalerTimestamp::Never => None, 156 TalerTimestamp::Timestamp(timestamp) => Some(timestamp.as_microsecond()), 157 } 158 .encode_by_ref(buf) 159 } 160 } 161 162 impl<'r> sqlx::Decode<'r, sqlx::Postgres> for TalerTimestamp { 163 fn decode( 164 value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>, 165 ) -> Result<Self, sqlx::error::BoxDynError> { 166 let micros = Option::<i64>::decode(value)?; 167 Ok(match micros { 168 None | Some(i64::MAX) => Self::Never, 169 Some(micros) => Self::Timestamp(Timestamp::from_microsecond(micros)?), 170 }) 171 } 172 } 173 174 /// <https://docs.taler.net/core/api-common.html#tsref-type-RelativeTime> 175 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 176 pub enum RelativeTime { 177 Forever, 178 Duration(Duration), 179 } 180 181 impl FromStr for RelativeTime { 182 type Err = anyhow::Error; 183 184 fn from_str(s: &str) -> Result<Self, Self::Err> { 185 if s == "forever" { 186 return Ok(Self::Forever); 187 } 188 let micros: u64 = s.parse()?; 189 190 Ok(Self::Duration(Duration::from_micros(micros))) 191 } 192 } 193 194 #[derive(Serialize, Deserialize)] 195 struct RelativeTimeImpl { 196 d_us: Value, 197 } 198 199 impl<'de> Deserialize<'de> for RelativeTime { 200 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 201 where 202 D: Deserializer<'de>, 203 { 204 let tmp = RelativeTimeImpl::deserialize(deserializer)?; 205 match tmp.d_us { 206 Value::Number(s) => { 207 if let Some(micros) = s.as_u64() { 208 Ok(Self::Duration(Duration::from_micros(micros))) 209 } else { 210 Err(Error::custom("Expected microseconds")) 211 } 212 } 213 Value::String(str) if str == "forever" => Ok(Self::Forever), 214 _ => Err(Error::custom("Expected time or 'forever'")), 215 } 216 } 217 } 218 219 impl Serialize for RelativeTime { 220 fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error> 221 where 222 S: Serializer, 223 { 224 let mut se_struct = se.serialize_struct("RelativeTime", 1)?; 225 match self { 226 RelativeTime::Forever => se_struct.serialize_field("d_us", "forever")?, 227 RelativeTime::Duration(duration) => { 228 se_struct.serialize_field("d_us", &duration.as_micros())? 229 } 230 } 231 se_struct.end() 232 } 233 } 234 235 impl Display for RelativeTime { 236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 237 match self { 238 RelativeTime::Forever => f.write_str("forever"), 239 RelativeTime::Duration(duration) => write!(f, "{duration:?}"), 240 } 241 } 242 }