Skip to content

Commit cd8a341

Browse files
author
Shawn L.
committed
feat: support jiff v0.2
1 parent 1e1f6bf commit cd8a341

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

postgres-types/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ with-eui48-1 = ["eui48-1"]
2323
with-geo-types-0_6 = ["geo-types-06"]
2424
with-geo-types-0_7 = ["geo-types-0_7"]
2525
with-jiff-0_1 = ["jiff-01"]
26+
with-jiff-0_2 = ["jiff-02"]
2627
with-serde_json-1 = ["serde-1", "serde_json-1"]
2728
with-smol_str-01 = ["smol_str-01"]
2829
with-uuid-0_8 = ["uuid-08"]
@@ -50,6 +51,7 @@ eui48-1 = { version = "1.0", package = "eui48", optional = true, default-feature
5051
geo-types-06 = { version = "0.6", package = "geo-types", optional = true }
5152
geo-types-0_7 = { version = "0.7", package = "geo-types", optional = true }
5253
jiff-01 = { version = "0.1", package = "jiff", optional = true }
54+
jiff-02 = { version = "0.2", package = "jiff", optional = true }
5355
serde-1 = { version = "1.0", package = "serde", optional = true }
5456
serde_json-1 = { version = "1.0", package = "serde_json", optional = true }
5557
uuid-08 = { version = "0.8", package = "uuid", optional = true }

postgres-types/src/jiff_02.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use bytes::BytesMut;
2+
use jiff_02::{
3+
civil::{Date, DateTime, Time},
4+
Span, SpanRound, Timestamp, Unit,
5+
};
6+
use postgres_protocol::types;
7+
use std::error::Error;
8+
9+
use crate::{FromSql, IsNull, ToSql, Type};
10+
11+
const fn base() -> DateTime {
12+
DateTime::constant(2000, 1, 1, 0, 0, 0, 0)
13+
}
14+
15+
/// The number of seconds from the Unix epoch to 2000-01-01 00:00:00 UTC.
16+
const PG_EPOCH: i64 = 946684800;
17+
18+
fn base_ts() -> Timestamp {
19+
Timestamp::new(PG_EPOCH, 0).unwrap()
20+
}
21+
22+
fn round_us<'a>() -> SpanRound<'a> {
23+
SpanRound::new().largest(Unit::Microsecond)
24+
}
25+
26+
fn decode_err<E>(_e: E) -> Box<dyn Error + Sync + Send>
27+
where
28+
E: Error,
29+
{
30+
"value too large to decode".into()
31+
}
32+
33+
fn transmit_err<E>(_e: E) -> Box<dyn Error + Sync + Send>
34+
where
35+
E: Error,
36+
{
37+
"value too large to transmit".into()
38+
}
39+
40+
impl<'a> FromSql<'a> for DateTime {
41+
fn from_sql(_: &Type, raw: &[u8]) -> Result<DateTime, Box<dyn Error + Sync + Send>> {
42+
let v = types::timestamp_from_sql(raw)?;
43+
Span::new()
44+
.try_microseconds(v)
45+
.and_then(|s| base().checked_add(s))
46+
.map_err(decode_err)
47+
}
48+
49+
accepts!(TIMESTAMP);
50+
}
51+
52+
impl ToSql for DateTime {
53+
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
54+
let v = self
55+
.since(base())
56+
.and_then(|s| s.round(round_us()))
57+
.map_err(transmit_err)?
58+
.get_microseconds();
59+
types::timestamp_to_sql(v, w);
60+
Ok(IsNull::No)
61+
}
62+
63+
accepts!(TIMESTAMP);
64+
to_sql_checked!();
65+
}
66+
67+
impl<'a> FromSql<'a> for Timestamp {
68+
fn from_sql(_: &Type, raw: &[u8]) -> Result<Timestamp, Box<dyn Error + Sync + Send>> {
69+
let v = types::timestamp_from_sql(raw)?;
70+
Span::new()
71+
.try_microseconds(v)
72+
.and_then(|s| base_ts().checked_add(s))
73+
.map_err(decode_err)
74+
}
75+
76+
accepts!(TIMESTAMPTZ);
77+
}
78+
79+
impl ToSql for Timestamp {
80+
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
81+
let v = self
82+
.since(base_ts())
83+
.and_then(|s| s.round(round_us()))
84+
.map_err(transmit_err)?
85+
.get_microseconds();
86+
types::timestamp_to_sql(v, w);
87+
Ok(IsNull::No)
88+
}
89+
90+
accepts!(TIMESTAMPTZ);
91+
to_sql_checked!();
92+
}
93+
94+
impl<'a> FromSql<'a> for Date {
95+
fn from_sql(_: &Type, raw: &[u8]) -> Result<Date, Box<dyn Error + Sync + Send>> {
96+
let v = types::date_from_sql(raw)?;
97+
Span::new()
98+
.try_days(v)
99+
.and_then(|s| base().date().checked_add(s))
100+
.map_err(decode_err)
101+
}
102+
accepts!(DATE);
103+
}
104+
105+
impl ToSql for Date {
106+
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
107+
let v = self.since(base().date()).map_err(transmit_err)?.get_days();
108+
types::date_to_sql(v, w);
109+
Ok(IsNull::No)
110+
}
111+
112+
accepts!(DATE);
113+
to_sql_checked!();
114+
}
115+
116+
impl<'a> FromSql<'a> for Time {
117+
fn from_sql(_: &Type, raw: &[u8]) -> Result<Time, Box<dyn Error + Sync + Send>> {
118+
let v = types::time_from_sql(raw)?;
119+
Span::new()
120+
.try_microseconds(v)
121+
.and_then(|s| Time::midnight().checked_add(s))
122+
.map_err(decode_err)
123+
}
124+
125+
accepts!(TIME);
126+
}
127+
128+
impl ToSql for Time {
129+
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
130+
let v = self
131+
.since(Time::midnight())
132+
.and_then(|s| s.round(round_us()))
133+
.map_err(transmit_err)?
134+
.get_microseconds();
135+
types::time_to_sql(v, w);
136+
Ok(IsNull::No)
137+
}
138+
139+
accepts!(TIME);
140+
to_sql_checked!();
141+
}

postgres-types/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ mod geo_types_06;
280280
mod geo_types_07;
281281
#[cfg(feature = "with-jiff-0_1")]
282282
mod jiff_01;
283+
#[cfg(feature = "with-jiff-0_2")]
284+
mod jiff_02;
283285
#[cfg(feature = "with-serde_json-1")]
284286
mod serde_json_1;
285287
#[cfg(feature = "with-smol_str-01")]

0 commit comments

Comments
 (0)