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, 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) -> IsNull {
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 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) -> IsNull {
87 PgInterval::try_from(*self)
88 .expect("failed to encode `std::time::Duration`")
89 .encode_by_ref(buf)
90 }
91
92 fn size_hint(&self) -> usize {
93 2 * mem::size_of::<i64>()
94 }
95}
96
97impl TryFrom<std::time::Duration> for PgInterval {
98 type Error = BoxDynError;
99
100 fn try_from(value: std::time::Duration) -> Result<Self, BoxDynError> {
105 if value.as_nanos() % 1000 != 0 {
106 return Err("PostgreSQL `INTERVAL` does not support nanoseconds precision".into());
107 }
108
109 Ok(Self {
110 months: 0,
111 days: 0,
112 microseconds: value.as_micros().try_into()?,
113 })
114 }
115}
116
117#[cfg(feature = "chrono")]
118impl Type<Postgres> for chrono::Duration {
119 fn type_info() -> PgTypeInfo {
120 PgTypeInfo::INTERVAL
121 }
122}
123
124#[cfg(feature = "chrono")]
125impl PgHasArrayType for chrono::Duration {
126 fn array_type_info() -> PgTypeInfo {
127 PgTypeInfo::INTERVAL_ARRAY
128 }
129}
130
131#[cfg(feature = "chrono")]
132impl Encode<'_, Postgres> for chrono::Duration {
133 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
134 let pg_interval = PgInterval::try_from(*self).expect("Failed to encode chrono::Duration");
135 pg_interval.encode_by_ref(buf)
136 }
137
138 fn size_hint(&self) -> usize {
139 2 * mem::size_of::<i64>()
140 }
141}
142
143#[cfg(feature = "chrono")]
144impl TryFrom<chrono::Duration> for PgInterval {
145 type Error = BoxDynError;
146
147 fn try_from(value: chrono::Duration) -> Result<Self, BoxDynError> {
152 value
153 .num_nanoseconds()
154 .map_or::<Result<_, Self::Error>, _>(
155 Err("Overflow has occurred for PostgreSQL `INTERVAL`".into()),
156 |nanoseconds| {
157 if nanoseconds % 1000 != 0 {
158 return Err(
159 "PostgreSQL `INTERVAL` does not support nanoseconds precision".into(),
160 );
161 }
162 Ok(())
163 },
164 )?;
165
166 value.num_microseconds().map_or(
167 Err("Overflow has occurred for PostgreSQL `INTERVAL`".into()),
168 |microseconds| {
169 Ok(Self {
170 months: 0,
171 days: 0,
172 microseconds: microseconds,
173 })
174 },
175 )
176 }
177}
178
179#[cfg(feature = "time")]
180impl Type<Postgres> for time::Duration {
181 fn type_info() -> PgTypeInfo {
182 PgTypeInfo::INTERVAL
183 }
184}
185
186#[cfg(feature = "time")]
187impl PgHasArrayType for time::Duration {
188 fn array_type_info() -> PgTypeInfo {
189 PgTypeInfo::INTERVAL_ARRAY
190 }
191}
192
193#[cfg(feature = "time")]
194impl Encode<'_, Postgres> for time::Duration {
195 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
196 let pg_interval = PgInterval::try_from(*self).expect("Failed to encode time::Duration");
197 pg_interval.encode_by_ref(buf)
198 }
199
200 fn size_hint(&self) -> usize {
201 2 * mem::size_of::<i64>()
202 }
203}
204
205#[cfg(feature = "time")]
206impl TryFrom<time::Duration> for PgInterval {
207 type Error = BoxDynError;
208
209 fn try_from(value: time::Duration) -> Result<Self, BoxDynError> {
214 if value.whole_nanoseconds() % 1000 != 0 {
215 return Err("PostgreSQL `INTERVAL` does not support nanoseconds precision".into());
216 }
217
218 Ok(Self {
219 months: 0,
220 days: 0,
221 microseconds: value.whole_microseconds().try_into()?,
222 })
223 }
224}
225
226#[test]
227fn test_encode_interval() {
228 let mut buf = PgArgumentBuffer::default();
229
230 let interval = PgInterval {
231 months: 0,
232 days: 0,
233 microseconds: 0,
234 };
235 assert!(matches!(
236 Encode::<Postgres>::encode(&interval, &mut buf),
237 IsNull::No
238 ));
239 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
240 buf.clear();
241
242 let interval = PgInterval {
243 months: 0,
244 days: 0,
245 microseconds: 1_000,
246 };
247 assert!(matches!(
248 Encode::<Postgres>::encode(&interval, &mut buf),
249 IsNull::No
250 ));
251 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0, 0]);
252 buf.clear();
253
254 let interval = PgInterval {
255 months: 0,
256 days: 0,
257 microseconds: 1_000_000,
258 };
259 assert!(matches!(
260 Encode::<Postgres>::encode(&interval, &mut buf),
261 IsNull::No
262 ));
263 assert_eq!(&**buf, [0, 0, 0, 0, 0, 15, 66, 64, 0, 0, 0, 0, 0, 0, 0, 0]);
264 buf.clear();
265
266 let interval = PgInterval {
267 months: 0,
268 days: 0,
269 microseconds: 3_600_000_000,
270 };
271 assert!(matches!(
272 Encode::<Postgres>::encode(&interval, &mut buf),
273 IsNull::No
274 ));
275 assert_eq!(
276 &**buf,
277 [0, 0, 0, 0, 214, 147, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0]
278 );
279 buf.clear();
280
281 let interval = PgInterval {
282 months: 0,
283 days: 1,
284 microseconds: 0,
285 };
286 assert!(matches!(
287 Encode::<Postgres>::encode(&interval, &mut buf),
288 IsNull::No
289 ));
290 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]);
291 buf.clear();
292
293 let interval = PgInterval {
294 months: 1,
295 days: 0,
296 microseconds: 0,
297 };
298 assert!(matches!(
299 Encode::<Postgres>::encode(&interval, &mut buf),
300 IsNull::No
301 ));
302 assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
303 buf.clear();
304
305 assert_eq!(
306 PgInterval::default(),
307 PgInterval {
308 months: 0,
309 days: 0,
310 microseconds: 0,
311 }
312 );
313}
314
315#[test]
316fn test_pginterval_std() {
317 let interval = PgInterval {
319 days: 0,
320 months: 0,
321 microseconds: 27_000,
322 };
323 assert_eq!(
324 &PgInterval::try_from(std::time::Duration::from_micros(27_000)).unwrap(),
325 &interval
326 );
327
328 assert!(PgInterval::try_from(std::time::Duration::from_nanos(27_000_001)).is_err());
330
331 assert!(PgInterval::try_from(std::time::Duration::from_secs(20_000_000_000_000)).is_err());
333}
334
335#[test]
336#[cfg(feature = "chrono")]
337fn test_pginterval_chrono() {
338 let interval = PgInterval {
340 days: 0,
341 months: 0,
342 microseconds: 27_000,
343 };
344 assert_eq!(
345 &PgInterval::try_from(chrono::Duration::microseconds(27_000)).unwrap(),
346 &interval
347 );
348
349 let interval = PgInterval {
351 days: 0,
352 months: 0,
353 microseconds: -27_000,
354 };
355 assert_eq!(
356 &PgInterval::try_from(chrono::Duration::microseconds(-27_000)).unwrap(),
357 &interval
358 );
359
360 assert!(PgInterval::try_from(chrono::Duration::nanoseconds(27_000_001)).is_err());
362 assert!(PgInterval::try_from(chrono::Duration::nanoseconds(-27_000_001)).is_err());
363
364 assert!(PgInterval::try_from(chrono::Duration::seconds(10_000_000_000)).is_err());
366 assert!(PgInterval::try_from(chrono::Duration::seconds(-10_000_000_000)).is_err());
367}
368
369#[test]
370#[cfg(feature = "time")]
371fn test_pginterval_time() {
372 let interval = PgInterval {
374 days: 0,
375 months: 0,
376 microseconds: 27_000,
377 };
378 assert_eq!(
379 &PgInterval::try_from(time::Duration::microseconds(27_000)).unwrap(),
380 &interval
381 );
382
383 let interval = PgInterval {
385 days: 0,
386 months: 0,
387 microseconds: -27_000,
388 };
389 assert_eq!(
390 &PgInterval::try_from(time::Duration::microseconds(-27_000)).unwrap(),
391 &interval
392 );
393
394 assert!(PgInterval::try_from(time::Duration::nanoseconds(27_000_001)).is_err());
396 assert!(PgInterval::try_from(time::Duration::nanoseconds(-27_000_001)).is_err());
397
398 assert!(PgInterval::try_from(time::Duration::seconds(10_000_000_000_000)).is_err());
400 assert!(PgInterval::try_from(time::Duration::seconds(-10_000_000_000_000)).is_err());
401}