rbdc_pg/types/
datetime.rs

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 rbdc::datetime::DateTime;
7use rbdc::Error;
8use std::io::Cursor;
9use std::str::FromStr;
10use std::time::Duration;
11
12/// Encode to Timestamptz
13impl Encode for DateTime {
14    fn encode(self, buf: &mut PgArgumentBuffer) -> Result<IsNull, Error> {
15        let mut millis = self.unix_timestamp_millis();
16        // Add session timezone offset to compensate for PostgreSQL's timezone conversion
17        if let Some(tz_sec) = buf.timezone_sec {
18            millis = millis + Duration::from_secs(tz_sec as u64).as_millis() as i64;
19        }
20        let epoch = fastdate::DateTime::from(fastdate::Date {
21            day: 1,
22            mon: 1,
23            year: 2000,
24        });
25        let dt = fastdate::DateTime::from_timestamp_millis(millis);
26        let micros;
27        if dt >= epoch {
28            micros = (dt - epoch).as_micros() as i64;
29        } else {
30            micros = (epoch - dt).as_micros() as i64 * -1;
31        }
32        micros.encode(buf)
33    }
34}
35
36impl Decode for DateTime {
37    fn decode(value: PgValue) -> Result<Self, Error> {
38        Ok(match value.format() {
39            PgValueFormat::Binary => {
40                let mut buf = Cursor::new(value.as_bytes()?);
41                let us = buf.read_i64::<BigEndian>()?;
42                let epoch = fastdate::DateTime::from(fastdate::Date {
43                    day: 1,
44                    mon: 1,
45                    year: 2000,
46                });
47                let v = {
48                    if us < 0 {
49                        epoch - std::time::Duration::from_micros(-us as u64)
50                    } else {
51                        epoch + std::time::Duration::from_micros(us as u64)
52                    }
53                };
54                let mut dt = DateTime(fastdate::DateTime::from_timestamp_millis(
55                    v.unix_timestamp_millis(),
56                ));
57                // Apply session timezone offset if available
58                if let Some(tz_sec) = value.timezone_sec {
59                    dt = dt.set_offset(tz_sec);
60                }
61                dt
62            }
63            PgValueFormat::Text => {
64                let s = value.as_str()?;
65                let date =
66                    fastdate::DateTime::from_str(s).map_err(|e| Error::from(e.to_string()))?;
67                DateTime(date)
68            }
69        })
70    }
71}