1use std::cmp::Ordering;
7use std::fmt::Debug;
8use std::fmt::Display;
9use std::fmt::Formatter;
10
11use jiff::SignedDuration;
12use jiff::civil::date;
13use jiff::fmt::strtime;
14use jiff::tz::Offset;
15
16const MICROS_PER_SEC: i64 = 1_000_000;
17const MICROS_PER_MINUTE: i64 = 60 * MICROS_PER_SEC;
18const MICROS_PER_HOUR: i64 = 60 * MICROS_PER_MINUTE;
19const MONTHS_PER_YEAR: i32 = 12;
20
21const TIMESTAMP_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f";
22const TIMESTAMP_TIMEZONE_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f %z";
23
24#[derive(Debug, Clone)]
30pub enum ExtensionValue<'a> {
31 Binary(&'a [u8]),
34 Date(Date),
36 Timestamp(Timestamp),
38 TimestampTz(TimestampTz),
40 Interval(Interval),
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
50pub struct Date {
51 pub value: i32,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
62pub struct Timestamp {
63 pub value: i64,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
74pub struct TimestampTz {
75 pub offset: i32,
77 pub value: i64,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
88pub struct Interval {
89 pub months: i32,
91 pub days: i32,
93 pub micros: i64,
95}
96
97impl Display for Date {
98 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
99 let dur = SignedDuration::from_hours(self.value as i64 * 24);
100 let date = date(1970, 1, 1).checked_add(dur).unwrap();
101 write!(f, "{}", date)
102 }
103}
104
105impl Display for Timestamp {
106 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
107 let micros = self.value;
108 let (mut secs, mut nanos) = (micros / MICROS_PER_SEC, (micros % MICROS_PER_SEC) * 1_000);
109 if nanos < 0 {
110 secs -= 1;
111 nanos += 1_000_000_000;
112 }
113
114 if secs > 253402207200 {
115 secs = 253402207200;
116 nanos = 0;
117 } else if secs < -377705023201 {
118 secs = -377705023201;
119 nanos = 0;
120 }
121 let ts = jiff::Timestamp::new(secs, nanos as i32).unwrap();
122
123 write!(f, "{}", strtime::format(TIMESTAMP_FORMAT, ts).unwrap())
124 }
125}
126
127impl Display for TimestampTz {
128 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
129 let micros = self.value;
130 let (mut secs, mut nanos) = (micros / MICROS_PER_SEC, (micros % MICROS_PER_SEC) * 1_000);
131 if nanos < 0 {
132 secs -= 1;
133 nanos += 1_000_000_000;
134 }
135
136 if secs > 253402207200 {
137 secs = 253402207200;
138 nanos = 0;
139 } else if secs < -377705023201 {
140 secs = -377705023201;
141 nanos = 0;
142 }
143 let ts = jiff::Timestamp::new(secs, nanos as i32).unwrap();
144 let tz_offset = Offset::from_seconds(self.offset).expect("invalid timezone offset seconds");
145 let tz = tz_offset.to_time_zone();
146 let zoned = ts.to_zoned(tz);
147
148 write!(
149 f,
150 "{}",
151 strtime::format(TIMESTAMP_TIMEZONE_FORMAT, &zoned).unwrap()
152 )
153 }
154}
155
156impl Display for Interval {
157 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
158 let mut wrote_date_part = false;
159 let years = self.months / MONTHS_PER_YEAR;
160 let months = self.months % MONTHS_PER_YEAR;
161
162 let mut write_component = |value: i32, singular: &str, plural: &str| -> std::fmt::Result {
163 if value != 0 {
164 if wrote_date_part {
165 write!(f, " ")?;
166 }
167 let abs_val = value.abs();
168 let unit = if abs_val == 1 { singular } else { plural };
169 if value < 0 {
170 write!(f, "-{} {}", abs_val, unit)?;
171 } else {
172 write!(f, "{} {}", abs_val, unit)?;
173 }
174 wrote_date_part = true;
175 }
176 Ok(())
177 };
178
179 write_component(years, "year", "years")?;
180 write_component(months, "month", "months")?;
181 write_component(self.days, "day", "days")?;
182
183 if self.micros != 0 {
184 if wrote_date_part {
185 write!(f, " ")?;
186 }
187 let mut micros = self.micros;
188 if micros < 0 {
189 write!(f, "-")?;
190 micros = -micros;
191 }
192 let hour = micros / MICROS_PER_HOUR;
193 micros -= hour * MICROS_PER_HOUR;
194 let min = micros / MICROS_PER_MINUTE;
195 micros -= min * MICROS_PER_MINUTE;
196 let sec = micros / MICROS_PER_SEC;
197 micros -= sec * MICROS_PER_SEC;
198
199 if hour < 100 {
200 write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?;
201 } else {
202 write!(f, "{}:{:02}:{:02}", hour, min, sec)?;
203 }
204 if micros != 0 {
205 write!(f, ".{:06}", micros)?;
206 }
207 } else if !wrote_date_part {
208 write!(f, "00:00:00")?;
209 }
210 Ok(())
211 }
212}
213
214impl Display for ExtensionValue<'_> {
215 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
216 match self {
217 ExtensionValue::Binary(v) => {
218 for c in *v {
219 write!(f, "{c:02X}")?;
220 }
221 Ok(())
222 }
223 ExtensionValue::Date(v) => write!(f, "{}", v),
224 ExtensionValue::Timestamp(v) => write!(f, "{}", v),
225 ExtensionValue::TimestampTz(v) => write!(f, "{}", v),
226 ExtensionValue::Interval(v) => write!(f, "{}", v),
227 }
228 }
229}
230
231impl Eq for ExtensionValue<'_> {}
232
233impl PartialEq for ExtensionValue<'_> {
234 fn eq(&self, other: &Self) -> bool {
235 self.partial_cmp(other) == Some(Ordering::Equal)
236 }
237}
238
239#[allow(clippy::non_canonical_partial_ord_impl)]
240impl PartialOrd for ExtensionValue<'_> {
241 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
242 let self_level = match self {
243 ExtensionValue::Binary(_) => 0,
244 ExtensionValue::Date(_) => 1,
245 ExtensionValue::Timestamp(_) => 2,
246 ExtensionValue::TimestampTz(_) => 3,
247 ExtensionValue::Interval(_) => 4,
248 };
249 let other_level = match other {
250 ExtensionValue::Binary(_) => 0,
251 ExtensionValue::Date(_) => 1,
252 ExtensionValue::Timestamp(_) => 2,
253 ExtensionValue::TimestampTz(_) => 3,
254 ExtensionValue::Interval(_) => 4,
255 };
256 let res = self_level.cmp(&other_level);
257 if matches!(res, Ordering::Greater | Ordering::Less) {
258 return Some(res);
259 }
260
261 match (self, other) {
262 (ExtensionValue::Binary(self_data), ExtensionValue::Binary(other_data)) => {
263 Some(self_data.cmp(other_data))
264 }
265 (ExtensionValue::Date(self_data), ExtensionValue::Date(other_data)) => {
266 Some(self_data.cmp(other_data))
267 }
268 (ExtensionValue::Timestamp(self_data), ExtensionValue::Timestamp(other_data)) => {
269 Some(self_data.cmp(other_data))
270 }
271 (ExtensionValue::TimestampTz(self_data), ExtensionValue::TimestampTz(other_data)) => {
272 Some(self_data.cmp(other_data))
273 }
274 (ExtensionValue::Interval(self_data), ExtensionValue::Interval(other_data)) => {
275 Some(self_data.cmp(other_data))
276 }
277 (_, _) => None,
278 }
279 }
280}