1use ocpp_core::DateTime;
7use sqlx::encode::IsNull;
8use sqlx::error::BoxDynError;
9use sqlx::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef};
10use sqlx::{Decode, Encode, Postgres, Type};
11
12const PG_EPOCH_OFFSET_MICROS: i64 = 946_684_800_000_000;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
16pub struct Ts(pub DateTime);
17
18impl From<DateTime> for Ts {
19 fn from(d: DateTime) -> Self {
20 Ts(d)
21 }
22}
23
24impl Type<Postgres> for Ts {
25 fn type_info() -> PgTypeInfo {
26 PgTypeInfo::with_name("timestamptz")
27 }
28}
29
30impl Encode<'_, Postgres> for Ts {
31 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
32 buf.extend_from_slice(&(self.0.unix_micros() - PG_EPOCH_OFFSET_MICROS).to_be_bytes());
33 Ok(IsNull::No)
34 }
35}
36
37impl<'r> Decode<'r, Postgres> for Ts {
38 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
39 match value.format() {
40 PgValueFormat::Binary => {
41 let b: [u8; 8] = value.as_bytes()?.try_into()?;
42 Ok(Ts(DateTime::from_unix_micros(
43 i64::from_be_bytes(b) + PG_EPOCH_OFFSET_MICROS,
44 )))
45 }
46 PgValueFormat::Text => Ok(Ts(DateTime::parse(value.as_str()?)?)),
47 }
48 }
49}
50
51impl serde::Serialize for Ts {
52 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
53 self.0.serialize(s)
54 }
55}
56
57impl<'de> serde::Deserialize<'de> for Ts {
58 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
59 DateTime::deserialize(d).map(Ts)
60 }
61}