mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-10-02 07:21:08 +00:00
feat(postgres) Add support for std::time::Duration, time::Duration & chrono::Duration
This commit is contained in:
parent
71cb68b2f4
commit
523f650340
@ -2,6 +2,9 @@ use std::mem;
|
|||||||
|
|
||||||
use byteorder::{NetworkEndian, ReadBytesExt};
|
use byteorder::{NetworkEndian, ReadBytesExt};
|
||||||
|
|
||||||
|
#[cfg(any(feature = "chrono", feature = "time"))]
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::decode::Decode;
|
use crate::decode::Decode;
|
||||||
use crate::encode::{Encode, IsNull};
|
use crate::encode::{Encode, IsNull};
|
||||||
use crate::error::BoxDynError;
|
use crate::error::BoxDynError;
|
||||||
@ -66,6 +69,222 @@ impl Encode<'_, Postgres> for PgInterval {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PgInterval {
|
||||||
|
/// Convert a `std::time::Duration` object to a `PgInterval` object but truncate the remaining nanoseconds.
|
||||||
|
///
|
||||||
|
/// Returns an error if there is a microseconds overflow.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use sqlx_core::postgres::types::PgInterval;
|
||||||
|
/// let interval = PgInterval::truncate_nanos_std(std::time::Duration::from_secs(3_600)).unwrap();
|
||||||
|
/// assert_eq!(interval, PgInterval { months: 0, days: 0, microseconds: 3_600_000_000 });
|
||||||
|
/// ```
|
||||||
|
pub fn truncate_nanos_std(value: std::time::Duration) -> Result<Self, BoxDynError> {
|
||||||
|
let microseconds = i64::try_from(value.as_micros())?;
|
||||||
|
Ok(Self {
|
||||||
|
months: 0,
|
||||||
|
days: 0,
|
||||||
|
microseconds,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Convert a `time::Duration` object to a `PgInterval` object but truncate the remaining nanoseconds.
|
||||||
|
///
|
||||||
|
/// Returns an error if there is a microseconds overflow.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use sqlx_core::postgres::types::PgInterval;
|
||||||
|
/// let interval = PgInterval::truncate_nanos_time(time::Duration::seconds(3_600)).unwrap();
|
||||||
|
/// assert_eq!(interval, PgInterval { months: 0, days: 0, microseconds: 3_600_000_000 });
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
pub fn truncate_nanos_time(value: time::Duration) -> Result<Self, BoxDynError> {
|
||||||
|
let microseconds = i64::try_from(value.whole_microseconds())?;
|
||||||
|
Ok(Self {
|
||||||
|
months: 0,
|
||||||
|
days: 0,
|
||||||
|
microseconds,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a `chrono::Duration` object to a `PgInterval` object but truncates the remaining nanoseconds.
|
||||||
|
/// Returns an error if there is a microseconds overflow.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use sqlx_core::postgres::types::PgInterval;
|
||||||
|
/// let interval = PgInterval::truncate_nanos_chrono(chrono::Duration::seconds(3_600)).unwrap();
|
||||||
|
/// assert_eq!(interval, PgInterval { months: 0, days: 0, microseconds: 3_600_000_000 });
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
|
pub fn truncate_nanos_chrono(value: chrono::Duration) -> Result<Self, BoxDynError> {
|
||||||
|
let microseconds = value.num_microseconds().ok_or("Microseconds overflow")?;
|
||||||
|
Ok(Self {
|
||||||
|
months: 0,
|
||||||
|
days: 0,
|
||||||
|
microseconds,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type<Postgres> for std::time::Duration {
|
||||||
|
fn type_info() -> PgTypeInfo {
|
||||||
|
PgTypeInfo::INTERVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type<Postgres> for [std::time::Duration] {
|
||||||
|
fn type_info() -> PgTypeInfo {
|
||||||
|
PgTypeInfo::INTERVAL_ARRAY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode<'_, Postgres> for std::time::Duration {
|
||||||
|
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||||
|
let pg_interval =
|
||||||
|
PgInterval::try_from(*self).expect("Failed to encode std::time::Duration");
|
||||||
|
pg_interval.encode_by_ref(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
2 * mem::size_of::<i64>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<std::time::Duration> for PgInterval {
|
||||||
|
type Error = BoxDynError;
|
||||||
|
|
||||||
|
/// Convert a `std::time::Duration` to a `PgInterval`
|
||||||
|
///
|
||||||
|
/// This returns an error if there is a loss of precision using nanoseconds or if there is a
|
||||||
|
/// microsecond overflow
|
||||||
|
///
|
||||||
|
/// To do lossy conversion use `PgInterval::truncate_nanos_std()`.
|
||||||
|
fn try_from(value: std::time::Duration) -> Result<Self, BoxDynError> {
|
||||||
|
match value.as_nanos() {
|
||||||
|
n if n % 1000 != 0 => {
|
||||||
|
Err("PostgreSQL INTERVAL does not support nanoseconds precision".into())
|
||||||
|
}
|
||||||
|
_ => Ok(Self {
|
||||||
|
months: 0,
|
||||||
|
days: 0,
|
||||||
|
microseconds: i64::try_from(value.as_micros())?,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
|
impl Type<Postgres> for chrono::Duration {
|
||||||
|
fn type_info() -> PgTypeInfo {
|
||||||
|
PgTypeInfo::INTERVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
|
impl Type<Postgres> for [chrono::Duration] {
|
||||||
|
fn type_info() -> PgTypeInfo {
|
||||||
|
PgTypeInfo::INTERVAL_ARRAY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
|
impl Encode<'_, Postgres> for chrono::Duration {
|
||||||
|
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||||
|
let pg_interval = PgInterval::try_from(*self).expect("Failed to encode chrono::Duration");
|
||||||
|
pg_interval.encode_by_ref(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
2 * mem::size_of::<i64>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
|
impl TryFrom<chrono::Duration> for PgInterval {
|
||||||
|
type Error = BoxDynError;
|
||||||
|
|
||||||
|
/// Convert a `chrono::Duration` to a `PgInterval`
|
||||||
|
///
|
||||||
|
/// This returns an error if there is a loss of precision using nanoseconds or if there is a
|
||||||
|
/// microsecond or nanosecond overflow
|
||||||
|
///
|
||||||
|
/// To do a lossy conversion use `PgInterval::truncate_nanos_chrono()`.
|
||||||
|
fn try_from(value: chrono::Duration) -> Result<Self, BoxDynError> {
|
||||||
|
let microseconds = value.num_microseconds().ok_or("Microseconds overflow")?;
|
||||||
|
match value
|
||||||
|
.checked_sub(&chrono::Duration::microseconds(microseconds))
|
||||||
|
.ok_or("Microseconds overflow")?
|
||||||
|
.num_nanoseconds()
|
||||||
|
.ok_or("Nanoseconds overflow")?
|
||||||
|
{
|
||||||
|
0 => Ok(Self {
|
||||||
|
months: 0,
|
||||||
|
days: 0,
|
||||||
|
microseconds,
|
||||||
|
}),
|
||||||
|
_ => Err("PostgreSQL INTERVAL does not support nanoseconds precision".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
impl Type<Postgres> for time::Duration {
|
||||||
|
fn type_info() -> PgTypeInfo {
|
||||||
|
PgTypeInfo::INTERVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
impl Type<Postgres> for [time::Duration] {
|
||||||
|
fn type_info() -> PgTypeInfo {
|
||||||
|
PgTypeInfo::INTERVAL_ARRAY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
impl Encode<'_, Postgres> for time::Duration {
|
||||||
|
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||||
|
let pg_interval = PgInterval::try_from(*self).expect("Failed to encode time::Duration");
|
||||||
|
pg_interval.encode_by_ref(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
2 * mem::size_of::<i64>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
impl TryFrom<time::Duration> for PgInterval {
|
||||||
|
type Error = BoxDynError;
|
||||||
|
|
||||||
|
/// Convert a `time::Duration` to a `PgInterval`
|
||||||
|
///
|
||||||
|
/// This returns an error if there is a loss of precision using nanoseconds or if there is a
|
||||||
|
/// microsecond overflow
|
||||||
|
///
|
||||||
|
/// To do a lossy conversion use `PgInterval::time_truncate_nanos()`.
|
||||||
|
fn try_from(value: time::Duration) -> Result<Self, BoxDynError> {
|
||||||
|
let microseconds = i64::try_from(value.whole_microseconds())?;
|
||||||
|
match value
|
||||||
|
.checked_sub(time::Duration::microseconds(microseconds))
|
||||||
|
.ok_or("Microseconds overflow")?
|
||||||
|
.subsec_nanoseconds()
|
||||||
|
{
|
||||||
|
0 => Ok(Self {
|
||||||
|
months: 0,
|
||||||
|
days: 0,
|
||||||
|
microseconds,
|
||||||
|
}),
|
||||||
|
_ => Err("PostgreSQL INTERVAL does not support nanoseconds precision".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encode_interval() {
|
fn test_encode_interval() {
|
||||||
let mut buf = PgArgumentBuffer::default();
|
let mut buf = PgArgumentBuffer::default();
|
||||||
@ -145,3 +364,44 @@ fn test_encode_interval() {
|
|||||||
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
assert_eq!(&**buf, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||||
buf.clear();
|
buf.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pginterval_std() {
|
||||||
|
let interval = PgInterval {
|
||||||
|
days: 0,
|
||||||
|
months: 0,
|
||||||
|
microseconds: 27_000,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
&PgInterval::try_from(std::time::Duration::from_micros(27_000)).unwrap(),
|
||||||
|
&interval
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
|
fn test_pginterval_chrono() {
|
||||||
|
let interval = PgInterval {
|
||||||
|
days: 0,
|
||||||
|
months: 0,
|
||||||
|
microseconds: 27_000,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
&PgInterval::try_from(chrono::Duration::microseconds(27_000)).unwrap(),
|
||||||
|
&interval
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
fn test_pginterval_time() {
|
||||||
|
let interval = PgInterval {
|
||||||
|
days: 0,
|
||||||
|
months: 0,
|
||||||
|
microseconds: 27_000,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
&PgInterval::try_from(time::Duration::microseconds(27_000)).unwrap(),
|
||||||
|
&interval
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user