1use crate::arguments::PgArgumentBuffer;
2use crate::types::decode::Decode;
3use crate::types::encode::{Encode, IsNull};
4use crate::value::{PgValue, PgValueFormat};
5use byteorder::{BigEndian, ReadBytesExt};
6use fastdate::time1::UtcOffset;
7use rbdc::Error;
8use rbs::Value;
9use std::fmt::{Debug, Display, Formatter};
10use std::io::Cursor;
11use std::time::Duration;
12
13#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
14#[serde(rename = "Timez")]
15pub struct Timetz(pub OffsetTz);
16
17#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
18#[serde(rename = "OffsetTz")]
19pub struct OffsetTz {
20 pub time: fastdate::Time,
21 pub offset: i32,
22}
23
24impl Display for OffsetTz {
25 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26 std::fmt::Display::fmt(&self.time, f)?;
27 let offset = UtcOffset::from_whole_seconds(self.offset).unwrap();
28 let (h, m, _) = offset.as_hms();
29 if h != 0 || m != 0 {
30 if self.offset >= 0 {
31 f.write_str("+")?;
32 } else {
33 f.write_str("-")?;
34 }
35 f.write_str(&format!("{:02}", h.abs()))?;
36 f.write_str(":")?;
37 f.write_str(&format!("{:02}", m.abs()))?;
38 }
39 Ok(())
40 }
41}
42
43impl Display for Timetz {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 write!(f, "{}", self.0)
46 }
47}
48
49impl From<Timetz> for Value {
50 fn from(arg: Timetz) -> Self {
51 rbs::value!(arg)
52 }
53}
54
55impl Decode for Timetz {
56 fn decode(value: PgValue) -> Result<Self, Error> {
57 match value.format() {
58 PgValueFormat::Binary => {
59 let mut buf = Cursor::new(value.as_bytes()?);
60
61 let microseconds = buf.read_i64::<BigEndian>()?;
63
64 let seconds = buf.read_i32::<BigEndian>()?;
66
67 Ok(Self(OffsetTz {
68 time: fastdate::Time::from(Duration::from_micros(microseconds as u64)),
69 offset: seconds,
70 }))
71 }
72 PgValueFormat::Text => {
73 Err("reading a `TIMETZ` value in text format is not supported.".into())
76 }
77 }
78 }
79}
80
81impl Encode for Timetz {
82 fn encode(self, buf: &mut PgArgumentBuffer) -> Result<IsNull, Error> {
83 let _ = (Duration::from(self.0.time).as_micros() as i64).encode(buf)?;
84 let _ = self.0.offset.encode(buf)?;
85 Ok(IsNull::No)
86 }
87}
88
89#[cfg(test)]
90mod test {
91 use crate::types::timetz::OffsetTz;
92 use fastdate::{offset_sec, Time};
93
94 #[test]
95 fn test_display() {
96 let o = OffsetTz {
97 time: Time {
98 nano: 0,
99 sec: 0,
100 minute: 0,
101 hour: 0,
102 },
103 offset: offset_sec(),
104 };
105
106 println!("{}", o);
107 }
108}