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