use jiff::fmt::temporal::DateTimeParser;
use sqlx_core::{
decode::Decode,
encode::{Encode, IsNull},
error::BoxDynError,
types::Type,
};
use sqlx_sqlite::{
Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef,
};
use crate::{Date, DateTime, Time, Timestamp, ToSqlx};
static PARSER: DateTimeParser = DateTimeParser::new();
impl Type<Sqlite> for Timestamp {
fn type_info() -> SqliteTypeInfo {
<str as Type<Sqlite>>::type_info()
}
fn compatible(ty: &SqliteTypeInfo) -> bool {
<str as Type<Sqlite>>::compatible(ty)
|| <f64 as Type<Sqlite>>::compatible(ty)
}
}
impl Encode<'_, Sqlite> for Timestamp {
fn encode_by_ref(
&self,
buf: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.to_jiff().to_string(), buf)
}
}
impl<'r> Decode<'r, Sqlite> for Timestamp {
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
let text = <&str as Decode<Sqlite>>::decode(value)?;
if text.contains(':') {
let date = PARSER.parse_timestamp(text)?;
return Ok(date.to_sqlx());
}
let julian_days = text.parse::<f64>()?;
julian_days_to_timestamp(julian_days).map(jiff::Timestamp::to_sqlx)
}
}
impl Type<Sqlite> for DateTime {
fn type_info() -> SqliteTypeInfo {
<str as Type<Sqlite>>::type_info()
}
}
impl Encode<'_, Sqlite> for DateTime {
fn encode_by_ref(
&self,
buf: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.to_jiff().to_string(), buf)
}
}
impl<'r> Decode<'r, Sqlite> for DateTime {
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
let text = <&[u8] as Decode<Sqlite>>::decode(value)?;
let date = PARSER.parse_datetime(text)?;
Ok(date.to_sqlx())
}
}
impl Type<Sqlite> for Date {
fn type_info() -> SqliteTypeInfo {
<str as Type<Sqlite>>::type_info()
}
}
impl Encode<'_, Sqlite> for Date {
fn encode_by_ref(
&self,
buf: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.to_jiff().to_string(), buf)
}
}
impl<'r> Decode<'r, Sqlite> for Date {
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
let text = <&[u8] as Decode<Sqlite>>::decode(value)?;
let date = PARSER.parse_date(text)?;
Ok(date.to_sqlx())
}
}
impl Type<Sqlite> for Time {
fn type_info() -> SqliteTypeInfo {
<str as Type<Sqlite>>::type_info()
}
}
impl Encode<'_, Sqlite> for Time {
fn encode_by_ref(
&self,
buf: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.to_jiff().to_string(), buf)
}
}
impl<'r> Decode<'r, Sqlite> for Time {
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
let text = <&[u8] as Decode<Sqlite>>::decode(value)?;
let date = PARSER.parse_time(text)?;
Ok(date.to_sqlx())
}
}
fn julian_days_to_timestamp(
days: f64,
) -> Result<jiff::Timestamp, BoxDynError> {
static UNIX_EPOCH_AS_JULIAN_DAYS: f64 = 2440587.5;
static SECONDS_PER_DAY: f64 = 86400.0;
let timestamp = (days - UNIX_EPOCH_AS_JULIAN_DAYS) * SECONDS_PER_DAY;
let sdur = jiff::SignedDuration::try_from_secs_f64(timestamp)?;
Ok(jiff::Timestamp::from_duration(sdur)?)
}