sentinel_driver/types/
timetz.rs1use bytes::{BufMut, BytesMut};
2
3use crate::error::{Error, Result};
4use crate::types::{FromSql, Oid, ToSql};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct PgTimeTz {
12 pub time: chrono::NaiveTime,
13 pub offset_seconds: i32,
14}
15
16impl ToSql for PgTimeTz {
17 fn oid(&self) -> Oid {
18 Oid::TIMETZ
19 }
20
21 #[allow(clippy::expect_used)]
22 fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
23 let midnight = chrono::NaiveTime::from_hms_opt(0, 0, 0).expect("midnight is valid");
24 let us = self
25 .time
26 .signed_duration_since(midnight)
27 .num_microseconds()
28 .unwrap_or(0);
29 buf.put_i64(us);
30 buf.put_i32(-self.offset_seconds);
32 Ok(())
33 }
34}
35
36impl FromSql for PgTimeTz {
37 fn oid() -> Oid {
38 Oid::TIMETZ
39 }
40
41 fn from_sql(buf: &[u8]) -> Result<Self> {
42 if buf.len() != 12 {
43 return Err(Error::Decode(format!(
44 "timetz: expected 12 bytes, got {}",
45 buf.len()
46 )));
47 }
48 let us = i64::from_be_bytes([
49 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
50 ]);
51 let pg_offset = i32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]);
52
53 let secs = (us / 1_000_000) as u32;
54 let micro = (us % 1_000_000) as u32;
55 let time = chrono::NaiveTime::from_num_seconds_from_midnight_opt(secs, micro * 1000)
56 .ok_or_else(|| {
57 Error::Decode(format!("timetz: time out of range: {us} microseconds"))
58 })?;
59
60 Ok(PgTimeTz {
61 time,
62 offset_seconds: -pg_offset,
64 })
65 }
66}