use bytes::BytesMut;
use postgres_types::{FromSql, IsNull, ToSql, Type};
use std::error::Error;
use crate::{Timestamp, TimestampError};
pub(crate) const POSTGRES_EPOCH_UNIX_SECS: i64 = 946_684_800;
impl ToSql for Timestamp {
fn to_sql(
&self,
_ty: &Type,
out: &mut BytesMut,
) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
let micros = (self.seconds() - POSTGRES_EPOCH_UNIX_SECS) * 1_000_000
+ i64::from(self.subsec_nanos() / 1_000);
out.extend_from_slice(µs.to_be_bytes());
Ok(IsNull::No)
}
postgres_types::accepts!(TIMESTAMPTZ);
postgres_types::to_sql_checked!();
}
impl<'a> FromSql<'a> for Timestamp {
fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
if raw.len() != 8 {
return Err(format!(
"invalid timestamptz length: expected 8 bytes, got {}",
raw.len()
)
.into());
}
let micros = i64::from_be_bytes(raw.try_into().unwrap());
let secs = micros.div_euclid(1_000_000) + POSTGRES_EPOCH_UNIX_SECS;
let nanos = (micros.rem_euclid(1_000_000) as u32) * 1_000;
Timestamp::from_unix(secs, nanos)
.map_err(|e: TimestampError| Box::new(e) as Box<dyn Error + Sync + Send>)
}
postgres_types::accepts!(TIMESTAMPTZ);
}