timestamp.rs (6830B)
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::{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 FromStr for TalerTimestamp { 31 type Err = anyhow::Error; 32 33 fn from_str(s: &str) -> Result<Self, Self::Err> { 34 if s == "never" { 35 return Ok(Self::Never); 36 } 37 let s: i64 = s.parse()?; 38 39 Ok(Self::Timestamp(jiff::Timestamp::from_second(s)?)) 40 } 41 } 42 43 #[derive(Serialize, Deserialize)] 44 struct TimestampImpl { 45 t_s: Value, 46 } 47 48 impl<'de> Deserialize<'de> for TalerTimestamp { 49 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 50 where 51 D: Deserializer<'de>, 52 { 53 let tmp = TimestampImpl::deserialize(deserializer)?; 54 match tmp.t_s { 55 Value::Number(s) => { 56 if let Some(since_epoch_s) = s.as_i64() { 57 jiff::Timestamp::from_second(since_epoch_s) 58 .map(Self::Timestamp) 59 .map_err(Error::custom) 60 } else { 61 Err(Error::custom("Expected epoch time")) 62 } 63 } 64 Value::String(str) if str == "never" => Ok(Self::Never), 65 _ => Err(Error::custom("Expected epoch time or 'never'")), 66 } 67 } 68 } 69 70 impl Serialize for TalerTimestamp { 71 fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error> 72 where 73 S: Serializer, 74 { 75 let mut se_struct = se.serialize_struct("Timestamp", 1)?; 76 match self { 77 TalerTimestamp::Never => se_struct.serialize_field("t_s", "never")?, 78 TalerTimestamp::Timestamp(timestamp) => { 79 se_struct.serialize_field("t_s", ×tamp.as_second())? 80 } 81 } 82 se_struct.end() 83 } 84 } 85 86 impl Display for TalerTimestamp { 87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 88 match self { 89 TalerTimestamp::Never => f.write_str("never"), 90 TalerTimestamp::Timestamp(timestamp) => timestamp.fmt(f), 91 } 92 } 93 } 94 95 impl From<jiff::Timestamp> for TalerTimestamp { 96 fn from(time: jiff::Timestamp) -> Self { 97 Self::Timestamp(time) 98 } 99 } 100 101 impl From<jiff::civil::Date> for TalerTimestamp { 102 fn from(date: jiff::civil::Date) -> Self { 103 date.to_datetime(Time::midnight()) 104 .to_zoned(TimeZone::UTC) 105 .unwrap() 106 .timestamp() 107 .into() 108 } 109 } 110 111 impl Add<jiff::Span> for TalerTimestamp { 112 type Output = Self; 113 114 fn add(self, rhs: jiff::Span) -> Self::Output { 115 match self { 116 TalerTimestamp::Never => TalerTimestamp::Never, 117 TalerTimestamp::Timestamp(timestamp) => TalerTimestamp::Timestamp(timestamp + rhs), 118 } 119 } 120 } 121 122 impl sqlx::Type<sqlx::Postgres> for TalerTimestamp { 123 fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo { 124 Option::<i64>::type_info() 125 } 126 } 127 128 impl<'q> sqlx::Encode<'q, sqlx::Postgres> for TalerTimestamp { 129 fn encode_by_ref( 130 &self, 131 buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>, 132 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> { 133 match self { 134 TalerTimestamp::Never => None, 135 TalerTimestamp::Timestamp(timestamp) => Some(timestamp.as_microsecond()), 136 } 137 .encode_by_ref(buf) 138 } 139 } 140 141 impl<'r> sqlx::Decode<'r, sqlx::Postgres> for TalerTimestamp { 142 fn decode( 143 value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>, 144 ) -> Result<Self, sqlx::error::BoxDynError> { 145 let micros = Option::<i64>::decode(value)?; 146 Ok(match micros { 147 Some(micros) => Self::Timestamp(Timestamp::from_microsecond(micros)?), 148 None => Self::Never, 149 }) 150 } 151 } 152 153 /// <https://docs.taler.net/core/api-common.html#tsref-type-RelativeTime> 154 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 155 pub enum RelativeTime { 156 Forever, 157 Duration(Duration), 158 } 159 160 impl FromStr for RelativeTime { 161 type Err = anyhow::Error; 162 163 fn from_str(s: &str) -> Result<Self, Self::Err> { 164 if s == "forever" { 165 return Ok(Self::Forever); 166 } 167 let micros: u64 = s.parse()?; 168 169 Ok(Self::Duration(Duration::from_micros(micros))) 170 } 171 } 172 173 #[derive(Serialize, Deserialize)] 174 struct RelativeTimeImpl { 175 d_us: Value, 176 } 177 178 impl<'de> Deserialize<'de> for RelativeTime { 179 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 180 where 181 D: Deserializer<'de>, 182 { 183 let tmp = RelativeTimeImpl::deserialize(deserializer)?; 184 match tmp.d_us { 185 Value::Number(s) => { 186 if let Some(micros) = s.as_u64() { 187 Ok(Self::Duration(Duration::from_micros(micros))) 188 } else { 189 Err(Error::custom("Expected microseconds")) 190 } 191 } 192 Value::String(str) if str == "forever" => Ok(Self::Forever), 193 _ => Err(Error::custom("Expected epoch time or 'forever'")), 194 } 195 } 196 } 197 198 impl Serialize for RelativeTime { 199 fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error> 200 where 201 S: Serializer, 202 { 203 let mut se_struct = se.serialize_struct("RelativeTime", 1)?; 204 match self { 205 RelativeTime::Forever => se_struct.serialize_field("d_us", "forever")?, 206 RelativeTime::Duration(duration) => { 207 se_struct.serialize_field("d_us", &duration.as_micros())? 208 } 209 } 210 se_struct.end() 211 } 212 } 213 214 impl Display for RelativeTime { 215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 216 match self { 217 RelativeTime::Forever => f.write_str("forever"), 218 RelativeTime::Duration(duration) => write!(f, "{duration:?}"), 219 } 220 } 221 }