Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support jiff as a date time lib backend #3487

Open
tisonkun opened this issue Sep 5, 2024 · 4 comments
Open

Support jiff as a date time lib backend #3487

tisonkun opened this issue Sep 5, 2024 · 4 comments
Labels
enhancement New feature or request

Comments

@tisonkun
Copy link

tisonkun commented Sep 5, 2024

As in https://docs.rs/sqlx/0.8.2/sqlx/postgres/types/index.html for chrono and time, being integrated with jiff.

Since the abstractions are similar, it should be viable.

cc @BurntSushi

I may take a closer look later but I can't commit it :P

@tisonkun tisonkun added the enhancement New feature or request label Sep 5, 2024
@abonander
Copy link
Collaborator

I have no strong opinions on jiff yet but I think I'd like to give it some time to mature. It's still in its first release cycle.

See also this comment: BurntSushi/jiff#50 (comment)

@tisonkun

This comment was marked as outdated.

@tisonkun

This comment was marked as outdated.

@tisonkun
Copy link
Author

For reference, here is a wrapper that can be used to bridge jiff's Timestamp to Postgres' TIMESTAMPTZ:

use std::str::FromStr;

use jiff::SignedDuration;
use serde::Deserialize;
use serde::Serialize;
use sqlx::encode::IsNull;
use sqlx::error::BoxDynError;
use sqlx::postgres::types::Oid;
use sqlx::postgres::PgArgumentBuffer;
use sqlx::postgres::PgHasArrayType;
use sqlx::postgres::PgTypeInfo;
use sqlx::postgres::PgValueFormat;
use sqlx::Database;
use sqlx::Decode;
use sqlx::Encode;
use sqlx::Postgres;
use sqlx::Type;

/// A module for Jiff support of SQLx.

// TODO(tisonkun): either switch to the upstream [1] or spawn a dedicate open-source crate.
// [1] https://github.com/launchbadge/sqlx/pull/3511
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Timestamp(pub jiff::Timestamp);

impl Type<Postgres> for Timestamp {
    fn type_info() -> PgTypeInfo {
        // 1184 => PgType::Timestamptz
        PgTypeInfo::with_oid(Oid(1184))
    }
}

impl PgHasArrayType for Timestamp {
    fn array_type_info() -> PgTypeInfo {
        // 1185 => PgType::TimestamptzArray
        PgTypeInfo::with_oid(Oid(1185))
    }
}

impl Encode<'_, Postgres> for Timestamp {
    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
        // TIMESTAMP is encoded as the microseconds since the epoch
        let micros = self
            .0
            .duration_since(postgres_epoch_timestamp())
            .as_micros();
        let micros = i64::try_from(micros)
            .map_err(|_| format!("Timestamp {} out of range for Postgres: {micros}", self.0))?;
        Encode::<Postgres>::encode(micros, buf)
    }

    fn size_hint(&self) -> usize {
        size_of::<i64>()
    }
}

impl<'r> Decode<'r, Postgres> for Timestamp {
    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
        Ok(match value.format() {
            PgValueFormat::Binary => {
                // TIMESTAMP is encoded as the microseconds since the epoch
                let us = Decode::<Postgres>::decode(value)?;
                let ts = postgres_epoch_timestamp().checked_add(SignedDuration::from_micros(us))?;
                Timestamp(ts)
            }
            PgValueFormat::Text => {
                let s = value.as_str()?;
                let ts = jiff::Timestamp::from_str(s)?;
                Timestamp(ts)
            }
        })
    }
}

fn postgres_epoch_timestamp() -> jiff::Timestamp {
    jiff::Timestamp::from_str("2000-01-01T00:00:00Z")
        .expect("2000-01-01T00:00:00Z is a valid timestamp")
}

For full integration if we are back to here and continue, sfackler/rust-postgres#1164 is a good reference for data type mapping and edge cases (error handling).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants