1use std::mem;
2
3use byteorder::{NetworkEndian, ReadBytesExt};
4
5use crate::decode::Decode;
6use crate::encode::{Encode, IsNull};
7use crate::error::BoxDynError;
8use crate::types::Type;
9use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
10
11#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Default)]
14pub struct PgInterval {
15 pub months: i32,
16 pub days: i32,
17 pub microseconds: i64,
18}
19
20impl Type<Postgres> for PgInterval {
21 fn type_info() -> PgTypeInfo {
22 PgTypeInfo::INTERVAL
23 }
24}
25
26impl PgHasArrayType for PgInterval {
27 fn array_type_info() -> PgTypeInfo {
28 PgTypeInfo::INTERVAL_ARRAY
29 }
30}
31
32impl<'de> Decode<'de, Postgres> for PgInterval {
33 fn decode(value: PgValueRef<'de>) -> Result<Self, BoxDynError> {
34 match value.format() {
35 PgValueFormat::Binary => {
36 let mut buf = value.as_bytes()?;
37 let microseconds = buf.read_i64::<NetworkEndian>()?;
38 let days = buf.read_i32::<NetworkEndian>()?;
39 let months = buf.read_i32::<NetworkEndian>()?;
40
41 Ok(PgInterval {
42 months,
43 days,
44 microseconds,
45 })
46 }
47
48 PgValueFormat::Text => {
50 Err("not implemented: decode `INTERVAL` in text mode (unprepared queries)".into())
51 }
52 }
53 }
54}
55
56impl Encode<'_, Postgres> for PgInterval {
57 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
58 buf.extend(&self.microseconds.to_be_bytes());
59 buf.extend(&self.days.to_be_bytes());
60 buf.extend(&self.months.to_be_bytes());
61
62 Ok(IsNull::No)
63 }
64
65 fn size_hint(&self) -> usize {
66 2 * mem::size_of::<i64>()
67 }
68}
69
70impl Type<Postgres> for std::time::Duration {
74 fn type_info() -> PgTypeInfo {
75 PgTypeInfo::INTERVAL
76 }
77}
78
79impl PgHasArrayType for std::time::Duration {
80 fn array_type_info() -> PgTypeInfo {
81 PgTypeInfo::INTERVAL_ARRAY
82 }
83}
84
85impl Encode<'_, Postgres> for std::time::Duration {
86 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
87 PgInterval::try_from(*self)?.encode_by_ref(buf)
88 }
89
90 fn size_hint(&self) -> usize {
91 2 * mem::size_of::<i64>()
92 }
93}
94
95impl TryFrom<std::time::Duration> for PgInterval {
96 type Error = BoxDynError;
97
98 fn try_from(value: std::time::Duration) -> Result<Self, BoxDynError> {
103 if value.as_nanos() % 1000 != 0 {
104 return Err("PostgreSQL `INTERVAL` does not support nanoseconds precision".into());
105 }
106
107 Ok(Self {
108 months: 0,
109 days: 0,
110 microseconds: value.as_micros().try_into()?,
111 })
112 }
113}
114
115#[cfg(feature = "chrono")]
116impl Type<Postgres> for chrono::Duration {
117 fn type_info() -> PgTypeInfo {
118 PgTypeInfo::INTERVAL
119 }
120}
121
122#[cfg(feature = "chrono")]
123impl PgHasArrayType for chrono::Duration {
124 fn array_type_info() -> PgTypeInfo {
125 PgTypeInfo::INTERVAL_ARRAY
126 }
127}
128
129#[cfg(feature = "chrono")]
130impl Encode<'_, Postgres> for chrono::Duration {
131 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
132 let pg_interval = PgInterval::try_from(*self)?;
133 pg_interval.encode_by_ref(buf)
134 }
135
136 fn size_hint(&self) -> usize {
137 2 * mem::size_of::<i64>()
138 }
139}
140
141#[cfg(feature = "chrono")]
142impl TryFrom<chrono::Duration> for PgInterval {
143 type Error = BoxDynError;
144
145 fn try_from(value: chrono::Duration) -> Result<Self, BoxDynError> {
150 value
151 .num_nanoseconds()
152 .map_or::<Result<_, Self::Error>, _>(
153 Err("Overflow has occurred for PostgreSQL `INTERVAL`".into()),
154 |nanoseconds| {
155 if nanoseconds % 1000 != 0 {
156 return Err(
157 "PostgreSQL `INTERVAL` does not support nanoseconds precision".into(),
158 );
159 }
160 Ok(())
161 },
162 )?;
163
164 value.num_microseconds().map_or(
165 Err("Overflow has occurred for PostgreSQL `INTERVAL`".into()),
166 |microseconds| {
167 Ok(Self {
168 months: 0,
169 days: 0,
170 microseconds,
171 })
172 },
173 )
174 }
175}
176
177#[cfg(feature = "time")]
178impl Type<Postgres> for time::Duration {
179 fn type_info() -> PgTypeInfo {
180 PgTypeInfo::INTERVAL
181 }
182}
183
184#[cfg(feature = "time")]
185impl PgHasArrayType for time::Duration {
186 fn array_type_info() -> PgTypeInfo {
187 PgTypeInfo::INTERVAL_ARRAY
188 }
189}
190
191#[cfg(feature = "time")]
192impl Encode<'_, Postgres> for time::Duration {
193 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
194 let pg_interval = PgInterval::try_from(*self)?;
195 pg_interval.encode_by_ref(buf)
196 }
197
198 fn size_hint(&self) -> usize {
199 2 * mem::size_of::<i64>()
200 }
201}
202
203#[cfg(feature = "time")]
204impl TryFrom<time::Duration> for PgInterval {
205 type Error = BoxDynError;
206
207 fn try_from(value: time::Duration) -> Result<Self, BoxDynError> {
212 if value.whole_nanoseconds() % 1000 != 0 {
213 return Err("PostgreSQL `INTERVAL` does not support nanoseconds precision".into());
214 }
215
216 Ok(Self {
217 months: 0,
218 days: 0,
219 microseconds: value.whole_microseconds().try_into()?,
220 })
221 }
222}
223
224#[test]
225fn test_encode_interval() {
226 let mut buf = PgArgumentBuffer::default();
227
228 let interval = PgInterval {
229 months: 0,
230 days: 0,
231 microseconds: 0,
232 };
233 assert!(matches!(
234 Encode::<Postgres>::encode(&interval, &mut buf),
235 Ok(IsNull::No)
236 ));
237 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
238 buf.clear();
239
240 let interval = PgInterval {
241 months: 0,
242 days: 0,
243 microseconds: 1_000,
244 };
245 assert!(matches!(
246 Encode::<Postgres>::encode(&interval, &mut buf),
247 Ok(IsNull::No)
248 ));
249 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0, 0]);
250 buf.clear();
251
252 let interval = PgInterval {
253 months: 0,
254 days: 0,
255 microseconds: 1_000_000,
256 };
257 assert!(matches!(
258 Encode::<Postgres>::encode(&interval, &mut buf),
259 Ok(IsNull::No)
260 ));
261 assert_eq!(&**buf, [0, 0, 0, 0, 0, 15, 66, 64, 0, 0, 0, 0, 0, 0, 0, 0]);
262 buf.clear();
263
264 let interval = PgInterval {
265 months: 0,
266 days: 0,
267 microseconds: 3_600_000_000,
268 };
269 assert!(matches!(
270 Encode::<Postgres>::encode(&interval, &mut buf),
271 Ok(IsNull::No)
272 ));
273 assert_eq!(
274 &**buf,
275 [0, 0, 0, 0, 214, 147, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0]
276 );
277 buf.clear();
278
279 let interval = PgInterval {
280 months: 0,
281 days: 1,
282 microseconds: 0,
283 };
284 assert!(matches!(
285 Encode::<Postgres>::encode(&interval, &mut buf),
286 Ok(IsNull::No)
287 ));
288 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]);
289 buf.clear();
290
291 let interval = PgInterval {
292 months: 1,
293 days: 0,
294 microseconds: 0,
295 };
296 assert!(matches!(
297 Encode::<Postgres>::encode(&interval, &mut buf),
298 Ok(IsNull::No)
299 ));
300 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
301 buf.clear();
302
303 assert_eq!(
304 PgInterval::default(),
305 PgInterval {
306 months: 0,
307 days: 0,
308 microseconds: 0,
309 }
310 );
311}
312
313#[test]
314fn test_pginterval_std() {
315 let interval = PgInterval {
317 days: 0,
318 months: 0,
319 microseconds: 27_000,
320 };
321 assert_eq!(
322 &PgInterval::try_from(std::time::Duration::from_micros(27_000)).unwrap(),
323 &interval
324 );
325
326 assert!(PgInterval::try_from(std::time::Duration::from_nanos(27_000_001)).is_err());
328
329 assert!(PgInterval::try_from(std::time::Duration::from_secs(20_000_000_000_000)).is_err());
331}
332
333#[test]
334#[cfg(feature = "chrono")]
335fn test_pginterval_chrono() {
336 let interval = PgInterval {
338 days: 0,
339 months: 0,
340 microseconds: 27_000,
341 };
342 assert_eq!(
343 &PgInterval::try_from(chrono::Duration::microseconds(27_000)).unwrap(),
344 &interval
345 );
346
347 let interval = PgInterval {
349 days: 0,
350 months: 0,
351 microseconds: -27_000,
352 };
353 assert_eq!(
354 &PgInterval::try_from(chrono::Duration::microseconds(-27_000)).unwrap(),
355 &interval
356 );
357
358 assert!(PgInterval::try_from(chrono::Duration::nanoseconds(27_000_001)).is_err());
360 assert!(PgInterval::try_from(chrono::Duration::nanoseconds(-27_000_001)).is_err());
361
362 assert!(PgInterval::try_from(chrono::Duration::seconds(10_000_000_000)).is_err());
364 assert!(PgInterval::try_from(chrono::Duration::seconds(-10_000_000_000)).is_err());
365}
366
367#[test]
368#[cfg(feature = "time")]
369fn test_pginterval_time() {
370 let interval = PgInterval {
372 days: 0,
373 months: 0,
374 microseconds: 27_000,
375 };
376 assert_eq!(
377 &PgInterval::try_from(time::Duration::microseconds(27_000)).unwrap(),
378 &interval
379 );
380
381 let interval = PgInterval {
383 days: 0,
384 months: 0,
385 microseconds: -27_000,
386 };
387 assert_eq!(
388 &PgInterval::try_from(time::Duration::microseconds(-27_000)).unwrap(),
389 &interval
390 );
391
392 assert!(PgInterval::try_from(time::Duration::nanoseconds(27_000_001)).is_err());
394 assert!(PgInterval::try_from(time::Duration::nanoseconds(-27_000_001)).is_err());
395
396 assert!(PgInterval::try_from(time::Duration::seconds(10_000_000_000_000)).is_err());
398 assert!(PgInterval::try_from(time::Duration::seconds(-10_000_000_000_000)).is_err());
399}