From 37ceb78aa7dc85ba276c5faa0a9646201a0d4690 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 2 Aug 2025 14:50:04 +0100 Subject: [PATCH 01/11] jiff sqlx binder --- sea-query-sqlx/Cargo.toml | 7 ++- sea-query-sqlx/src/sqlx_postgres.rs | 93 ++++++++++++++++++++++++++++- sea-query-sqlx/src/sqlx_sqlite.rs | 20 +++++++ 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/sea-query-sqlx/Cargo.toml b/sea-query-sqlx/Cargo.toml index 6d1a1b763..b1b8348d7 100644 --- a/sea-query-sqlx/Cargo.toml +++ b/sea-query-sqlx/Cargo.toml @@ -21,11 +21,13 @@ sea-query = { version = "1.0.0-rc.30", path = "..", default-features = false, fe sqlx = { version = "0.8", default-features = false, optional = true } ipnetwork = { version = "0.20", default-features = false, optional = true } # pin dependency pgvector = { version = "0.4", default-features = false, optional = true } # requires feature flag +jiff = { version = "0.2.15", default-features = false, optional = true, features = ["std", "perf-inline"] } +jiff-sqlx = { version = "0.1", optional = true } [features] sqlx-mysql = ["sqlx/mysql"] -sqlx-postgres = ["sqlx/postgres", "sea-query/backend-postgres"] -sqlx-sqlite = ["sqlx/sqlite"] +sqlx-postgres = ["sqlx/postgres", "sea-query/backend-postgres", "jiff-sqlx?/postgres"] +sqlx-sqlite = ["sqlx/sqlite", "jiff-sqlx?/sqlite"] sqlx-any = ["sqlx/any"] with-chrono = ["sqlx?/chrono", "sea-query/with-chrono"] with-json = ["sqlx?/json", "sea-query/with-json"] @@ -35,6 +37,7 @@ with-uuid = ["sqlx?/uuid", "sea-query/with-uuid"] with-time = ["sqlx?/time", "sea-query/with-time"] with-ipnetwork = ["sqlx?/ipnetwork", "sea-query/with-ipnetwork"] with-mac_address = ["sqlx?/mac_address", "sea-query/with-mac_address"] +with-jiff = ["sea-query/with-jiff", "jiff", "jiff-sqlx"] postgres-array = ["sea-query/postgres-array"] postgres-vector = ["sea-query/postgres-vector", "pgvector/sqlx"] runtime-async-std = ["sqlx?/runtime-async-std"] diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index defaddc31..52208cace 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -15,7 +15,9 @@ use sea_query::prelude::time; #[cfg(feature = "with-chrono")] use sea_query::prelude::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; -use sea_query::{ArrayType, OptionEnum, Value}; +#[cfg(feature = "postgres-array")] +use sea_query::{ArrayType, ValueType}; +use sea_query::{OptionEnum, Value}; use crate::SqlxValues; @@ -114,6 +116,26 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { Value::TimeDateTimeWithTimeZone(t) => { let _ = args.add(t); } + #[cfg(feature = "with-jiff")] + Value::JiffDate(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffTime(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffDateTime(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffTimestamp(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffZoned(_) => { + unimplemented!("no support by jiff-sqlx"); + } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { let _ = args.add(uuid); @@ -302,6 +324,75 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { ); let _ = args.add(value); } + #[cfg(feature = "with-jiff")] + ArrayType::JiffDate => { + let value = match v { + Some(j) => Some( + j.into_iter() + .map(|j| { + jiff_sqlx::ToSqlx::to_sqlx( + ::try_from(j).unwrap(), + ) + }) + .collect::>(), + ), + None => None, + }; + let _ = args.add(value); + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffTime => { + let value = match v { + Some(j) => Some( + j.into_iter() + .map(|j| { + jiff_sqlx::ToSqlx::to_sqlx( + ::try_from(j).unwrap(), + ) + }) + .collect::>(), + ), + None => None, + }; + let _ = args.add(value); + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffDateTime => { + let value = match v { + Some(j) => Some( + j.into_iter() + .map(|j| { + jiff_sqlx::ToSqlx::to_sqlx( + ::try_from(j) + .unwrap(), + ) + }) + .collect::>(), + ), + None => None, + }; + let _ = args.add(value); + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffTimestamp => { + let value = match v { + Some(j) => Some( + j.into_iter() + .map(|j| { + jiff_sqlx::ToSqlx::to_sqlx( + ::try_from(j).unwrap(), + ) + }) + .collect::>(), + ), + None => None, + }; + let _ = args.add(value); + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffZoned => { + unimplemented!("no support by jiff-sqlx"); + } #[cfg(feature = "with-uuid")] ArrayType::Uuid => { let value: Option> = Value::Array(ty, v) diff --git a/sea-query-sqlx/src/sqlx_sqlite.rs b/sea-query-sqlx/src/sqlx_sqlite.rs index d6ea1b96f..09818f5e8 100644 --- a/sea-query-sqlx/src/sqlx_sqlite.rs +++ b/sea-query-sqlx/src/sqlx_sqlite.rs @@ -97,6 +97,26 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { Value::TimeDateTimeWithTimeZone(t) => { let _ = args.add(t); } + #[cfg(feature = "with-jiff")] + Value::JiffDate(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffTime(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffDateTime(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffTimestamp(j) => { + let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); + } + #[cfg(feature = "with-jiff")] + Value::JiffZoned(_) => { + unimplemented!("no support by jiff-sqlx"); + } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { let _ = args.add(uuid); From 5ae9219e4a91e59a610377ddbab108dfc0241482 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 2 Aug 2025 14:50:21 +0100 Subject: [PATCH 02/11] example --- examples/sqlx_postgres/Cargo.toml | 2 ++ examples/sqlx_postgres/src/main.rs | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/sqlx_postgres/Cargo.toml b/examples/sqlx_postgres/Cargo.toml index bdf2cf50b..fcdf9ae89 100644 --- a/examples/sqlx_postgres/Cargo.toml +++ b/examples/sqlx_postgres/Cargo.toml @@ -10,6 +10,7 @@ publish = false [dependencies] time = { version = "0.3.36", features = ["macros"] } +jiff = { version = "0.2.15", features = ["std"] } uuid = { version = "1", features = ["serde", "v4"] } serde_json = { version = "1" } async-std = { version = "1.8", features = [ "attributes" ] } @@ -23,6 +24,7 @@ sea-query-sqlx = { path = "../../sea-query-sqlx", features = [ "with-bigdecimal", "with-uuid", "with-time", + "with-jiff", "with-ipnetwork", "with-mac_address", "runtime-async-std-native-tls", diff --git a/examples/sqlx_postgres/src/main.rs b/examples/sqlx_postgres/src/main.rs index 4d1f02fdc..5746d8d61 100644 --- a/examples/sqlx_postgres/src/main.rs +++ b/examples/sqlx_postgres/src/main.rs @@ -20,9 +20,16 @@ async fn main() { // Schema + let sql = Table::drop() + .table(Character::Table) + .if_exists() + .build(PostgresQueryBuilder); + + let result = sqlx::query(&sql).execute(&mut *pool).await; + println!("Drop table character: {result:?}\n"); + let sql = Table::create() .table(Character::Table) - .if_not_exists() .col( ColumnDef::new(Character::Id) .integer() @@ -72,11 +79,12 @@ async fn main() { .unwrap() .with_scale(3) .into(), - NaiveDate::from_ymd_opt(2020, 8, 20) - .unwrap() - .and_hms_opt(0, 0, 0) - .unwrap() - .into(), + // NaiveDate::from_ymd_opt(2020, 8, 20) + // .unwrap() + // .and_hms_opt(0, 0, 0) + // .unwrap() + // .into(), + jiff::civil::date(2020, 8, 20).at(0, 0, 0, 0).into(), IpNetwork::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8) .unwrap() .into(), From 984074dfff987f3d1dc3b85e57be179ebdd24b01 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Fri, 20 Mar 2026 01:48:04 +0800 Subject: [PATCH 03/11] Add feature guard --- Cargo.toml | 1 + sea-query-sqlx/Cargo.toml | 2 ++ sea-query-sqlx/src/lib.rs | 11 ++++++++++ sea-query-sqlx/src/sqlx_any.rs | 31 ++++++++++++++++++++++++++--- sea-query-sqlx/src/sqlx_mysql.rs | 31 ++++++++++++++++++++++++++--- sea-query-sqlx/src/sqlx_postgres.rs | 20 ++++++++++++++----- sea-query-sqlx/src/sqlx_sqlite.rs | 13 ++++++++---- 7 files changed, 94 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2ba3cf6e5..483e0d545 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ with-bigdecimal = ["bigdecimal"] with-uuid = ["uuid"] with-time = ["time"] with-jiff = ["jiff"] +unimplemented-jiff-zoned = [] with-ipnetwork = ["ipnetwork"] with-mac_address = ["mac_address"] sqlx-utils = ["derive"] diff --git a/sea-query-sqlx/Cargo.toml b/sea-query-sqlx/Cargo.toml index b1b8348d7..47efc140d 100644 --- a/sea-query-sqlx/Cargo.toml +++ b/sea-query-sqlx/Cargo.toml @@ -38,6 +38,8 @@ with-time = ["sqlx?/time", "sea-query/with-time"] with-ipnetwork = ["sqlx?/ipnetwork", "sea-query/with-ipnetwork"] with-mac_address = ["sqlx?/mac_address", "sea-query/with-mac_address"] with-jiff = ["sea-query/with-jiff", "jiff", "jiff-sqlx"] +unimplemented-jiff-sqlx-mysql = [] +unimplemented-jiff-zoned = ["sea-query/unimplemented-jiff-zoned"] postgres-array = ["sea-query/postgres-array"] postgres-vector = ["sea-query/postgres-vector", "pgvector/sqlx"] runtime-async-std = ["sqlx?/runtime-async-std"] diff --git a/sea-query-sqlx/src/lib.rs b/sea-query-sqlx/src/lib.rs index 482897bbc..adf519c9e 100644 --- a/sea-query-sqlx/src/lib.rs +++ b/sea-query-sqlx/src/lib.rs @@ -9,6 +9,17 @@ //! //! [1]: ../sqlx/fn.query_with.html +#[cfg(all( + any(feature = "sqlx-mysql", feature = "sqlx-any"), + feature = "with-jiff", + not(feature = "unimplemented-jiff-sqlx-mysql") +))] +const _: () = panic!( + "sea-query-sqlx does not support with-jiff together with sqlx-mysql/sqlx-any yet; \ +enable the `unimplemented-jiff-sqlx-mysql` feature to acknowledge the limitation and keep the \ +current runtime panic behavior" +); + #[cfg(feature = "sqlx-any")] mod sqlx_any; #[cfg(feature = "sqlx-mysql")] diff --git a/sea-query-sqlx/src/sqlx_any.rs b/sea-query-sqlx/src/sqlx_any.rs index 7e064e927..7438469f4 100644 --- a/sea-query-sqlx/src/sqlx_any.rs +++ b/sea-query-sqlx/src/sqlx_any.rs @@ -99,6 +99,26 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::any::Any> for SqlxValues { let _ = args.add(Value::TimeDateTimeWithTimeZone(t).time_as_naive_utc_in_string()); } + #[cfg(feature = "with-jiff")] + Value::JiffDate(_) => { + panic!("Jiff support not implemented for Any"); + } + #[cfg(feature = "with-jiff")] + Value::JiffTime(_) => { + panic!("Jiff support not implemented for Any"); + } + #[cfg(feature = "with-jiff")] + Value::JiffDateTime(_) => { + panic!("Jiff support not implemented for Any"); + } + #[cfg(feature = "with-jiff")] + Value::JiffTimestamp(_) => { + panic!("Jiff support not implemented for Any"); + } + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] + Value::JiffZoned(_) => { + panic!("Jiff support not implemented for Any"); + } #[cfg(feature = "with-uuid")] Value::Uuid(_) => { panic!("UUID support not implemented for Any"); @@ -131,9 +151,14 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::any::Any> for SqlxValues { Value::Vector(_) => { panic!("SQLx doesn't support vector arguments for Any"); } /* #[cfg(feature = "postgres-range")] - Value::Range(_) => { - panic!("SQLx doesn't support PgRange arguments for Any"); - } */ + Value::Range(_) => { + panic!("SQLx doesn't support PgRange arguments for Any"); + } */ + #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] + other => { + let _ = other; + panic!("JiffZoned support not implemented for Any"); + } } } args diff --git a/sea-query-sqlx/src/sqlx_mysql.rs b/sea-query-sqlx/src/sqlx_mysql.rs index 749171939..648da8716 100644 --- a/sea-query-sqlx/src/sqlx_mysql.rs +++ b/sea-query-sqlx/src/sqlx_mysql.rs @@ -97,6 +97,26 @@ impl sqlx::IntoArguments<'_, sqlx::mysql::MySql> for SqlxValues { Value::TimeDateTimeWithTimeZone(t) => { let _ = args.add(t); } + #[cfg(feature = "with-jiff")] + Value::JiffDate(_) => { + panic!("Mysql doesn't support JiffDate arguments"); + } + #[cfg(feature = "with-jiff")] + Value::JiffTime(_) => { + panic!("Mysql doesn't support JiffTime arguments"); + } + #[cfg(feature = "with-jiff")] + Value::JiffDateTime(_) => { + panic!("Mysql doesn't support JiffDateTime arguments"); + } + #[cfg(feature = "with-jiff")] + Value::JiffTimestamp(_) => { + panic!("Mysql doesn't support JiffTimestamp arguments"); + } + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] + Value::JiffZoned(_) => { + panic!("Mysql doesn't support JiffZoned arguments"); + } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { let _ = args.add(uuid); @@ -129,9 +149,14 @@ impl sqlx::IntoArguments<'_, sqlx::mysql::MySql> for SqlxValues { Value::MacAddress(_) => { panic!("Mysql doesn't support MacAddress arguments"); } /* #[cfg(feature = "postgres-range")] - Value::Range(_) => { - panic!("Mysql doesn't support PgRange arguments"); - } */ + Value::Range(_) => { + panic!("Mysql doesn't support PgRange arguments"); + } */ + #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] + other => { + let _ = other; + panic!("Mysql doesn't support JiffZoned arguments"); + } } } args diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index 52208cace..85860d135 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -132,7 +132,7 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { Value::JiffTimestamp(j) => { let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); } - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(_) => { unimplemented!("no support by jiff-sqlx"); } @@ -389,7 +389,7 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { }; let _ = args.add(value); } - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] ArrayType::JiffZoned => { unimplemented!("no support by jiff-sqlx"); } @@ -429,14 +429,24 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { .expect("This Value::Array should consist of Value::MacAddress"); let _ = args.add(value); } + #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] + _ => { + let _ = ty; + panic!("Postgres doesn't support JiffZoned array arguments"); + } }, #[cfg(feature = "postgres-vector")] Value::Vector(v) => { let _ = args.add(v); } /* #[cfg(feature = "postgres-range")] - Value::Range(v) => { - let _ = args.add(v); - } */ + Value::Range(v) => { + let _ = args.add(v); + } */ + #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] + other => { + let _ = other; + panic!("Postgres doesn't support JiffZoned arguments"); + } } } args diff --git a/sea-query-sqlx/src/sqlx_sqlite.rs b/sea-query-sqlx/src/sqlx_sqlite.rs index 09818f5e8..0e71cd420 100644 --- a/sea-query-sqlx/src/sqlx_sqlite.rs +++ b/sea-query-sqlx/src/sqlx_sqlite.rs @@ -113,7 +113,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { Value::JiffTimestamp(j) => { let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); } - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(_) => { unimplemented!("no support by jiff-sqlx"); } @@ -150,9 +150,14 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { Value::Vector(_) => { panic!("Sqlite doesn't support vector arguments"); } /* #[cfg(feature = "postgres-range")] - Value::Range(_) => { - panic!("Sqlite doesn't support PgRange arguments"); - } */ + Value::Range(_) => { + panic!("Sqlite doesn't support PgRange arguments"); + } */ + #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] + other => { + let _ = other; + panic!("Sqlite doesn't support JiffZoned arguments"); + } } } args From d2c839b969e71062114a638645e7eb7893c5478f Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Fri, 20 Mar 2026 16:15:43 +0800 Subject: [PATCH 04/11] Map jiff::timestamp to timestampz --- src/backend/query_builder.rs | 1 - src/value/with_jiff.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 215004be7..e85354114 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1352,7 +1352,6 @@ pub trait QueryBuilder: write!(buf, "{v}")?; buf.write_str("'")?; } - // Both JiffDateTime and JiffTimestamp map to timestamp #[cfg(feature = "with-jiff")] Value::JiffDateTime(Some(v)) => { use crate::with_jiff::JIFF_DATE_TIME_FMT_STR; diff --git a/src/value/with_jiff.rs b/src/value/with_jiff.rs index 80a188afb..331cbde80 100644 --- a/src/value/with_jiff.rs +++ b/src/value/with_jiff.rs @@ -4,7 +4,7 @@ use jiff::{Timestamp, Zoned, civil}; type_to_value!(civil::Date, JiffDate, Date); type_to_value!(civil::Time, JiffTime, Time); type_to_box_value!(civil::DateTime, JiffDateTime, DateTime); -type_to_box_value!(Timestamp, JiffTimestamp, Timestamp); +type_to_box_value!(Timestamp, JiffTimestamp, TimestampWithTimeZone); type_to_box_value!(Zoned, JiffZoned, TimestampWithTimeZone); impl DateLikeValue for civil::Date {} @@ -104,7 +104,7 @@ impl Value { } pub(crate) const JIFF_DATE_TIME_FMT_STR: &str = "%Y-%m-%d %H:%M:%S%.6f"; -pub(crate) const JIFF_TIMESTAMP_FMT_STR: &str = "%Y-%m-%d %H:%M:%S%.6f"; +pub(crate) const JIFF_TIMESTAMP_FMT_STR: &str = "%Y-%m-%d %H:%M:%S%.6f %:z"; pub(crate) const JIFF_ZONE_FMT_STR: &str = "%Y-%m-%d %H:%M:%S%.6f %:z"; impl Value { @@ -152,7 +152,7 @@ mod tests { assert_eq!( Value::jiff_timestamp(jiff::Timestamp::constant(0, 123456 * 1000)) .jiff_value_to_string(), - Some("1970-01-01 00:00:00.123456".to_owned()) + Some("1970-01-01 00:00:00.123456 +00:00".to_owned()) ); assert_eq!( From 710207e6ed4405d498a6d2313dce41aef9c71d8e Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Sat, 21 Mar 2026 19:30:45 +0800 Subject: [PATCH 05/11] Use consistent SQLite text encoding for Jiff datetime values --- sea-query-sqlx/src/sqlx_postgres.rs | 11 +++--- sea-query-sqlx/src/sqlx_sqlite.rs | 11 +++--- src/backend/query_builder.rs | 13 +++---- src/value.rs | 2 +- src/value/with_jiff.rs | 56 ++--------------------------- 5 files changed, 19 insertions(+), 74 deletions(-) diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index 85860d135..33e96d6b7 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -429,9 +429,11 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { .expect("This Value::Array should consist of Value::MacAddress"); let _ = args.add(value); } - #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] - _ => { - let _ = ty; + #[cfg(all( + feature = "with-jiff", + not(feature = "unimplemented-jiff-zoned") + ))] + ArrayType::JiffZoned => { panic!("Postgres doesn't support JiffZoned array arguments"); } }, @@ -443,8 +445,7 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { let _ = args.add(v); } */ #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] - other => { - let _ = other; + Value::JiffZoned(_) => { panic!("Postgres doesn't support JiffZoned arguments"); } } diff --git a/sea-query-sqlx/src/sqlx_sqlite.rs b/sea-query-sqlx/src/sqlx_sqlite.rs index 0e71cd420..4068e9999 100644 --- a/sea-query-sqlx/src/sqlx_sqlite.rs +++ b/sea-query-sqlx/src/sqlx_sqlite.rs @@ -99,19 +99,19 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { } #[cfg(feature = "with-jiff")] Value::JiffDate(j) => { - let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(j))); + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(feature = "with-jiff")] Value::JiffTime(j) => { - let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(j))); + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(feature = "with-jiff")] Value::JiffDateTime(j) => { - let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(feature = "with-jiff")] Value::JiffTimestamp(j) => { - let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(_) => { @@ -154,8 +154,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { panic!("Sqlite doesn't support PgRange arguments"); } */ #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] - other => { - let _ = other; + Value::JiffZoned(_) => { panic!("Sqlite doesn't support JiffZoned arguments"); } } diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index e85354114..b0f5ed9e5 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1338,8 +1338,6 @@ pub trait QueryBuilder: buf.write_str(&v.format(time_format::FORMAT_DATETIME_TZ).unwrap())?; buf.write_str("'")?; } - // Jiff date and time dosen't need format string - // The default behavior is what we want #[cfg(feature = "with-jiff")] Value::JiffDate(Some(v)) => { buf.write_str("'")?; @@ -1354,24 +1352,21 @@ pub trait QueryBuilder: } #[cfg(feature = "with-jiff")] Value::JiffDateTime(Some(v)) => { - use crate::with_jiff::JIFF_DATE_TIME_FMT_STR; buf.write_str("'")?; - write!(buf, "{}", v.strftime(JIFF_DATE_TIME_FMT_STR))?; + write!(buf, "{v}")?; buf.write_str("'")?; } #[cfg(feature = "with-jiff")] Value::JiffTimestamp(Some(v)) => { - use crate::with_jiff::JIFF_TIMESTAMP_FMT_STR; buf.write_str("'")?; - write!(buf, "{}", v.strftime(JIFF_TIMESTAMP_FMT_STR))?; + write!(buf, "{v}")?; buf.write_str("'")?; } #[cfg(feature = "with-jiff")] - // Zoned map to timestamp with timezone + // Zoned keeps Jiff's canonical RFC 9557 textual form. Value::JiffZoned(Some(v)) => { - use crate::with_jiff::JIFF_ZONE_FMT_STR; buf.write_str("'")?; - write!(buf, "{}", v.strftime(JIFF_ZONE_FMT_STR))?; + write!(buf, "{v}")?; buf.write_str("'")?; } #[cfg(feature = "with-rust_decimal")] diff --git a/src/value.rs b/src/value.rs index c385bb943..68a7bd05d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -42,7 +42,7 @@ mod with_time; #[cfg(feature = "with-jiff")] #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] -pub(crate) mod with_jiff; +pub mod with_jiff; #[cfg(feature = "with-rust_decimal")] #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] diff --git a/src/value/with_jiff.rs b/src/value/with_jiff.rs index 331cbde80..fcab9adae 100644 --- a/src/value/with_jiff.rs +++ b/src/value/with_jiff.rs @@ -103,67 +103,17 @@ impl Value { } } -pub(crate) const JIFF_DATE_TIME_FMT_STR: &str = "%Y-%m-%d %H:%M:%S%.6f"; -pub(crate) const JIFF_TIMESTAMP_FMT_STR: &str = "%Y-%m-%d %H:%M:%S%.6f %:z"; -pub(crate) const JIFF_ZONE_FMT_STR: &str = "%Y-%m-%d %H:%M:%S%.6f %:z"; - impl Value { #[cfg(test)] pub(crate) fn jiff_value_to_string(&self) -> Option { match self { Self::JiffDate(v) => v.as_ref().map(|v| v.to_string()), Self::JiffTime(v) => v.as_ref().map(|v| v.to_string()), - Self::JiffDateTime(v) => v - .as_ref() - .map(|v| v.strftime(JIFF_DATE_TIME_FMT_STR).to_string()), - Self::JiffTimestamp(v) => v - .as_ref() - .map(|v| v.strftime(JIFF_TIMESTAMP_FMT_STR).to_string()), - Self::JiffZoned(v) => v - .as_ref() - .map(|v| v.strftime(JIFF_ZONE_FMT_STR).to_string()), + Self::JiffDateTime(v) => v.as_ref().map(|v| v.to_string()), + Self::JiffTimestamp(v) => v.as_ref().map(|v| v.to_string()), + Self::JiffZoned(v) => v.as_ref().map(|v| v.to_string()), _ => panic!("not jiff Value"), } } } -#[cfg(test)] -mod tests { - - use jiff::fmt::strtime; - - #[test] - fn jiff_fmt() { - use super::*; - assert_eq!( - Value::jiff_date(jiff::civil::date(2020, 1, 1)).jiff_value_to_string(), - Some("2020-01-01".to_owned()) - ); - assert_eq!( - Value::jiff_time(jiff::civil::time(1, 2, 3, 123456 * 1000)).jiff_value_to_string(), - Some("01:02:03.123456".to_owned()) - ); - assert_eq!( - Value::jiff_date_time(jiff::civil::date(2020, 1, 1).at(1, 2, 3, 123456 * 1000)) - .jiff_value_to_string(), - Some("2020-01-01 01:02:03.123456".to_owned()) - ); - - assert_eq!( - Value::jiff_timestamp(jiff::Timestamp::constant(0, 123456 * 1000)) - .jiff_value_to_string(), - Some("1970-01-01 00:00:00.123456 +00:00".to_owned()) - ); - - assert_eq!( - Value::jiff_zoned( - strtime::parse(JIFF_ZONE_FMT_STR, "1970-01-01 00:00:00.123456 +00:00") - .unwrap() - .to_zoned() - .unwrap() - ) - .jiff_value_to_string(), - Some("1970-01-01 00:00:00.123456 +00:00".to_owned()) - ); - } -} From b316b5b05a72fa96476d171e3e7e6167d75cf869 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Sun, 22 Mar 2026 00:11:14 +0800 Subject: [PATCH 06/11] Changlog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dedf0790..7ab2d578a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,10 @@ pub struct ColumnName(pub Option, pub DynIden); ### Enhancements +* Supports Jiff types except `jiff::Zoned`. + At the moment this support is only available through the SQLx binder. + When using SQLx with multiple backends, enable `unimplemented-jiff-sqlx-mysql` to suppress the + compile-time error. After enabling it, runtime panics may occur. * Add `Expr::not_exists` https://github.com/SeaQL/sea-query/pull/983 * Add `serde` feature. Currently, enabling it allows `Value` to be serializable https://github.com/SeaQL/sea-query/pull/966 * Add `Keyword::Default` https://github.com/SeaQL/sea-query/pull/965 From 56a209793394d55a7f7b4809228151893317679e Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Sun, 22 Mar 2026 19:12:11 +0800 Subject: [PATCH 07/11] Update feature guard --- sea-query-sqlx/src/sqlx_any.rs | 27 +++++++++------------- sea-query-sqlx/src/sqlx_mysql.rs | 11 +++------ sea-query-sqlx/src/sqlx_postgres.rs | 21 +++++------------ sea-query-sqlx/src/sqlx_sqlite.rs | 12 ++++------ src/backend/query_builder.rs | 4 ++-- src/value.rs | 35 ++++++++++++++++++++--------- src/value/hashable_value.rs | 4 ++-- src/value/with_jiff.rs | 12 ++++++++-- src/value/with_json.rs | 2 +- 9 files changed, 63 insertions(+), 65 deletions(-) diff --git a/sea-query-sqlx/src/sqlx_any.rs b/sea-query-sqlx/src/sqlx_any.rs index 7438469f4..0e7a8ffaa 100644 --- a/sea-query-sqlx/src/sqlx_any.rs +++ b/sea-query-sqlx/src/sqlx_any.rs @@ -100,20 +100,20 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::any::Any> for SqlxValues { args.add(Value::TimeDateTimeWithTimeZone(t).time_as_naive_utc_in_string()); } #[cfg(feature = "with-jiff")] - Value::JiffDate(_) => { - panic!("Jiff support not implemented for Any"); + Value::JiffDate(j) => { + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(feature = "with-jiff")] - Value::JiffTime(_) => { - panic!("Jiff support not implemented for Any"); + Value::JiffTime(j) => { + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(feature = "with-jiff")] - Value::JiffDateTime(_) => { - panic!("Jiff support not implemented for Any"); + Value::JiffDateTime(j) => { + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(feature = "with-jiff")] - Value::JiffTimestamp(_) => { - panic!("Jiff support not implemented for Any"); + Value::JiffTimestamp(j) => { + let _ = args.add(j.map(|j| j.to_string())); } #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(_) => { @@ -151,14 +151,9 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::any::Any> for SqlxValues { Value::Vector(_) => { panic!("SQLx doesn't support vector arguments for Any"); } /* #[cfg(feature = "postgres-range")] - Value::Range(_) => { - panic!("SQLx doesn't support PgRange arguments for Any"); - } */ - #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] - other => { - let _ = other; - panic!("JiffZoned support not implemented for Any"); - } + Value::Range(_) => { + panic!("SQLx doesn't support PgRange arguments for Any"); + } */ } } args diff --git a/sea-query-sqlx/src/sqlx_mysql.rs b/sea-query-sqlx/src/sqlx_mysql.rs index 648da8716..f5eda900b 100644 --- a/sea-query-sqlx/src/sqlx_mysql.rs +++ b/sea-query-sqlx/src/sqlx_mysql.rs @@ -149,14 +149,9 @@ impl sqlx::IntoArguments<'_, sqlx::mysql::MySql> for SqlxValues { Value::MacAddress(_) => { panic!("Mysql doesn't support MacAddress arguments"); } /* #[cfg(feature = "postgres-range")] - Value::Range(_) => { - panic!("Mysql doesn't support PgRange arguments"); - } */ - #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] - other => { - let _ = other; - panic!("Mysql doesn't support JiffZoned arguments"); - } + Value::Range(_) => { + panic!("Mysql doesn't support PgRange arguments"); + } */ } } args diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index 33e96d6b7..25f8f4857 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -134,7 +134,7 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { } #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(_) => { - unimplemented!("no support by jiff-sqlx"); + panic!("Postgres doesn't support JiffZoned arguments"); } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { @@ -389,10 +389,6 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { }; let _ = args.add(value); } - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - ArrayType::JiffZoned => { - unimplemented!("no support by jiff-sqlx"); - } #[cfg(feature = "with-uuid")] ArrayType::Uuid => { let value: Option> = Value::Array(ty, v) @@ -429,10 +425,7 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { .expect("This Value::Array should consist of Value::MacAddress"); let _ = args.add(value); } - #[cfg(all( - feature = "with-jiff", - not(feature = "unimplemented-jiff-zoned") - ))] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] ArrayType::JiffZoned => { panic!("Postgres doesn't support JiffZoned array arguments"); } @@ -441,13 +434,9 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { Value::Vector(v) => { let _ = args.add(v); } /* #[cfg(feature = "postgres-range")] - Value::Range(v) => { - let _ = args.add(v); - } */ - #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] - Value::JiffZoned(_) => { - panic!("Postgres doesn't support JiffZoned arguments"); - } + Value::Range(v) => { + let _ = args.add(v); + } */ } } args diff --git a/sea-query-sqlx/src/sqlx_sqlite.rs b/sea-query-sqlx/src/sqlx_sqlite.rs index 4068e9999..b8a7b59de 100644 --- a/sea-query-sqlx/src/sqlx_sqlite.rs +++ b/sea-query-sqlx/src/sqlx_sqlite.rs @@ -115,7 +115,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { } #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(_) => { - unimplemented!("no support by jiff-sqlx"); + panic!("Sqlite doesn't support JiffZoned arguments"); } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { @@ -150,13 +150,9 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { Value::Vector(_) => { panic!("Sqlite doesn't support vector arguments"); } /* #[cfg(feature = "postgres-range")] - Value::Range(_) => { - panic!("Sqlite doesn't support PgRange arguments"); - } */ - #[cfg(all(feature = "with-jiff", not(feature = "unimplemented-jiff-zoned")))] - Value::JiffZoned(_) => { - panic!("Sqlite doesn't support JiffZoned arguments"); - } + Value::Range(_) => { + panic!("Sqlite doesn't support PgRange arguments"); + } */ } } args diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index b0f5ed9e5..60b0dde63 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1222,7 +1222,7 @@ pub trait QueryBuilder: Value::JiffDateTime(None) => buf.write_str("NULL")?, #[cfg(feature = "with-jiff")] Value::JiffTimestamp(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(None) => buf.write_str("NULL")?, #[cfg(feature = "with-rust_decimal")] Value::Decimal(None) => buf.write_str("NULL")?, @@ -1362,7 +1362,7 @@ pub trait QueryBuilder: write!(buf, "{v}")?; buf.write_str("'")?; } - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] // Zoned keeps Jiff's canonical RFC 9557 textual form. Value::JiffZoned(Some(v)) => { buf.write_str("'")?; diff --git a/src/value.rs b/src/value.rs index 68a7bd05d..173143c3f 100644 --- a/src/value.rs +++ b/src/value.rs @@ -162,8 +162,11 @@ pub enum ArrayType { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] JiffTimestamp, - #[cfg(feature = "with-jiff")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) + )] JiffZoned, #[cfg(feature = "with-uuid")] @@ -298,8 +301,11 @@ pub enum Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] JiffTimestamp(Option>), - #[cfg(feature = "with-jiff")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) + )] JiffZoned(Option>), #[cfg(feature = "with-uuid")] @@ -465,8 +471,11 @@ impl Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] Self::JiffTimestamp(_) => Self::JiffTimestamp(None), - #[cfg(feature = "with-jiff")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) + )] Self::JiffZoned(_) => Self::JiffZoned(None), #[cfg(feature = "with-uuid")] @@ -605,8 +614,11 @@ impl Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] Self::JiffTimestamp(_) => Self::JiffTimestamp(Some(Timestamp::UNIX_EPOCH.into())), - #[cfg(feature = "with-jiff")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) + )] Self::JiffZoned(_) => Self::JiffZoned(Some( Timestamp::UNIX_EPOCH .to_zoned(jiff::tz::TimeZone::UTC) @@ -744,8 +756,11 @@ impl Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] Self::JiffTimestamp(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())), - #[cfg(feature = "with-jiff")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) + )] Self::JiffZoned(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())), #[cfg(feature = "with-uuid")] diff --git a/src/value/hashable_value.rs b/src/value/hashable_value.rs index 15c9b7321..cb78b3ad3 100644 --- a/src/value/hashable_value.rs +++ b/src/value/hashable_value.rs @@ -57,7 +57,7 @@ impl PartialEq for Value { (Self::JiffDateTime(l), Self::JiffDateTime(r)) => l == r, #[cfg(feature = "with-jiff")] (Self::JiffTimestamp(l), Self::JiffTimestamp(r)) => l == r, - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] (Self::JiffZoned(l), Self::JiffZoned(r)) => l == r, #[cfg(feature = "with-uuid")] @@ -143,7 +143,7 @@ impl Hash for Value { Value::JiffDateTime(datetime) => datetime.hash(state), #[cfg(feature = "with-jiff")] Value::JiffTimestamp(timestamp) => timestamp.hash(state), - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(zoned) => zoned.hash(state), #[cfg(feature = "with-uuid")] diff --git a/src/value/with_jiff.rs b/src/value/with_jiff.rs index fcab9adae..6f2bf3bb4 100644 --- a/src/value/with_jiff.rs +++ b/src/value/with_jiff.rs @@ -1,22 +1,27 @@ use super::*; -use jiff::{Timestamp, Zoned, civil}; +#[cfg(feature = "unimplemented-jiff-zoned")] +use jiff::Zoned; +use jiff::{Timestamp, civil}; type_to_value!(civil::Date, JiffDate, Date); type_to_value!(civil::Time, JiffTime, Time); type_to_box_value!(civil::DateTime, JiffDateTime, DateTime); type_to_box_value!(Timestamp, JiffTimestamp, TimestampWithTimeZone); +#[cfg(feature = "unimplemented-jiff-zoned")] type_to_box_value!(Zoned, JiffZoned, TimestampWithTimeZone); impl DateLikeValue for civil::Date {} impl TimeLikeValue for civil::Time {} impl DateTimeLikeValue for civil::DateTime {} impl DateTimeLikeValue for Timestamp {} +#[cfg(feature = "unimplemented-jiff-zoned")] impl DateTimeLikeValue for Zoned {} impl DateLikeValueNullable for Option {} impl TimeLikeValueNullable for Option {} impl DateTimeLikeValueNullable for Option {} impl DateTimeLikeValueNullable for Option {} +#[cfg(feature = "unimplemented-jiff-zoned")] impl DateTimeLikeValueNullable for Option {} impl Value { @@ -40,6 +45,7 @@ impl Value { Value::JiffTimestamp(v.into().map(Into::into)) } + #[cfg(feature = "unimplemented-jiff-zoned")] #[inline] pub fn jiff_zoned>>(v: T) -> Value { Value::JiffZoned(v.into().map(Into::into)) @@ -63,6 +69,7 @@ impl Value { matches!(self, Self::JiffTimestamp(_)) } + #[cfg(feature = "unimplemented-jiff-zoned")] pub fn is_jiff_zoned(&self) -> bool { matches!(self, Self::JiffZoned(_)) } @@ -95,6 +102,7 @@ impl Value { } } + #[cfg(feature = "unimplemented-jiff-zoned")] pub fn as_ref_jiff_zoned(&self) -> Option<&Zoned> { match self { Self::JiffZoned(v) => v.as_deref(), @@ -111,9 +119,9 @@ impl Value { Self::JiffTime(v) => v.as_ref().map(|v| v.to_string()), Self::JiffDateTime(v) => v.as_ref().map(|v| v.to_string()), Self::JiffTimestamp(v) => v.as_ref().map(|v| v.to_string()), + #[cfg(feature = "unimplemented-jiff-zoned")] Self::JiffZoned(v) => v.as_ref().map(|v| v.to_string()), _ => panic!("not jiff Value"), } } } - diff --git a/src/value/with_json.rs b/src/value/with_json.rs index 62a4ec6f1..9bfcab7d3 100644 --- a/src/value/with_json.rs +++ b/src/value/with_json.rs @@ -95,7 +95,7 @@ pub fn sea_value_to_json_value(value: &Value) -> Json { Value::JiffDateTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), #[cfg(feature = "with-jiff")] Value::JiffTimestamp(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-jiff")] + #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] Value::JiffZoned(_) => CommonSqlQueryBuilder.value_to_string(value).into(), #[cfg(feature = "with-rust_decimal")] Value::Decimal(Some(v)) => { From 84eecbf52afe90b4ff4a5c507dff2dc4ec6768fb Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Sun, 22 Mar 2026 20:37:14 +0800 Subject: [PATCH 08/11] Add missing NotU8 impls --- src/value/postgres_array.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/value/postgres_array.rs b/src/value/postgres_array.rs index a47db20a8..bf86459de 100644 --- a/src/value/postgres_array.rs +++ b/src/value/postgres_array.rs @@ -47,6 +47,21 @@ impl NotU8 for PrimitiveDateTime {} #[cfg(feature = "with-time")] impl NotU8 for OffsetDateTime {} +#[cfg(feature = "with-jiff")] +impl NotU8 for jiff::civil::Date {} + +#[cfg(feature = "with-jiff")] +impl NotU8 for jiff::civil::Time {} + +#[cfg(feature = "with-jiff")] +impl NotU8 for jiff::civil::DateTime {} + +#[cfg(feature = "with-jiff")] +impl NotU8 for jiff::Timestamp {} + +#[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] +impl NotU8 for jiff::Zoned {} + #[cfg(feature = "with-rust_decimal")] impl NotU8 for Decimal {} From 8ef64d07aec72d8c5b2401da8bf10675eccbf4f3 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Mon, 23 Mar 2026 02:37:01 +0800 Subject: [PATCH 09/11] Remove jiff::Zoned --- Cargo.toml | 1 - sea-query-sqlx/Cargo.toml | 1 - sea-query-sqlx/src/sqlx_any.rs | 4 --- sea-query-sqlx/src/sqlx_mysql.rs | 4 --- sea-query-sqlx/src/sqlx_postgres.rs | 8 ------ sea-query-sqlx/src/sqlx_sqlite.rs | 5 ---- src/backend/query_builder.rs | 9 ------- src/value.rs | 39 ----------------------------- src/value/hashable_value.rs | 4 --- src/value/postgres_array.rs | 3 --- src/value/prelude.rs | 2 +- src/value/with_jiff.rs | 29 --------------------- src/value/with_json.rs | 2 -- 13 files changed, 1 insertion(+), 110 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 483e0d545..2ba3cf6e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,6 @@ with-bigdecimal = ["bigdecimal"] with-uuid = ["uuid"] with-time = ["time"] with-jiff = ["jiff"] -unimplemented-jiff-zoned = [] with-ipnetwork = ["ipnetwork"] with-mac_address = ["mac_address"] sqlx-utils = ["derive"] diff --git a/sea-query-sqlx/Cargo.toml b/sea-query-sqlx/Cargo.toml index 47efc140d..5ba5dccbd 100644 --- a/sea-query-sqlx/Cargo.toml +++ b/sea-query-sqlx/Cargo.toml @@ -39,7 +39,6 @@ with-ipnetwork = ["sqlx?/ipnetwork", "sea-query/with-ipnetwork"] with-mac_address = ["sqlx?/mac_address", "sea-query/with-mac_address"] with-jiff = ["sea-query/with-jiff", "jiff", "jiff-sqlx"] unimplemented-jiff-sqlx-mysql = [] -unimplemented-jiff-zoned = ["sea-query/unimplemented-jiff-zoned"] postgres-array = ["sea-query/postgres-array"] postgres-vector = ["sea-query/postgres-vector", "pgvector/sqlx"] runtime-async-std = ["sqlx?/runtime-async-std"] diff --git a/sea-query-sqlx/src/sqlx_any.rs b/sea-query-sqlx/src/sqlx_any.rs index 0e7a8ffaa..544491791 100644 --- a/sea-query-sqlx/src/sqlx_any.rs +++ b/sea-query-sqlx/src/sqlx_any.rs @@ -115,10 +115,6 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::any::Any> for SqlxValues { Value::JiffTimestamp(j) => { let _ = args.add(j.map(|j| j.to_string())); } - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - Value::JiffZoned(_) => { - panic!("Jiff support not implemented for Any"); - } #[cfg(feature = "with-uuid")] Value::Uuid(_) => { panic!("UUID support not implemented for Any"); diff --git a/sea-query-sqlx/src/sqlx_mysql.rs b/sea-query-sqlx/src/sqlx_mysql.rs index f5eda900b..ee83ec401 100644 --- a/sea-query-sqlx/src/sqlx_mysql.rs +++ b/sea-query-sqlx/src/sqlx_mysql.rs @@ -113,10 +113,6 @@ impl sqlx::IntoArguments<'_, sqlx::mysql::MySql> for SqlxValues { Value::JiffTimestamp(_) => { panic!("Mysql doesn't support JiffTimestamp arguments"); } - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - Value::JiffZoned(_) => { - panic!("Mysql doesn't support JiffZoned arguments"); - } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { let _ = args.add(uuid); diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index 25f8f4857..e8f68d665 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -132,10 +132,6 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { Value::JiffTimestamp(j) => { let _ = args.add(j.map(|j| jiff_sqlx::ToSqlx::to_sqlx(*j))); } - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - Value::JiffZoned(_) => { - panic!("Postgres doesn't support JiffZoned arguments"); - } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { let _ = args.add(uuid); @@ -425,10 +421,6 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { .expect("This Value::Array should consist of Value::MacAddress"); let _ = args.add(value); } - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - ArrayType::JiffZoned => { - panic!("Postgres doesn't support JiffZoned array arguments"); - } }, #[cfg(feature = "postgres-vector")] Value::Vector(v) => { diff --git a/sea-query-sqlx/src/sqlx_sqlite.rs b/sea-query-sqlx/src/sqlx_sqlite.rs index b8a7b59de..e9c26ab94 100644 --- a/sea-query-sqlx/src/sqlx_sqlite.rs +++ b/sea-query-sqlx/src/sqlx_sqlite.rs @@ -113,10 +113,6 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { Value::JiffTimestamp(j) => { let _ = args.add(j.map(|j| j.to_string())); } - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - Value::JiffZoned(_) => { - panic!("Sqlite doesn't support JiffZoned arguments"); - } #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => { let _ = args.add(uuid); @@ -127,7 +123,6 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { } #[cfg(feature = "with-bigdecimal")] Value::BigDecimal(big_decimal) => { - use sea_query::prelude::bigdecimal::ToPrimitive; let _ = args.add(big_decimal.map(|d| d.to_string())); } #[cfg(feature = "with-json")] diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 60b0dde63..250237755 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1222,8 +1222,6 @@ pub trait QueryBuilder: Value::JiffDateTime(None) => buf.write_str("NULL")?, #[cfg(feature = "with-jiff")] Value::JiffTimestamp(None) => buf.write_str("NULL")?, - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - Value::JiffZoned(None) => buf.write_str("NULL")?, #[cfg(feature = "with-rust_decimal")] Value::Decimal(None) => buf.write_str("NULL")?, #[cfg(feature = "with-bigdecimal")] @@ -1362,13 +1360,6 @@ pub trait QueryBuilder: write!(buf, "{v}")?; buf.write_str("'")?; } - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - // Zoned keeps Jiff's canonical RFC 9557 textual form. - Value::JiffZoned(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{v}")?; - buf.write_str("'")?; - } #[cfg(feature = "with-rust_decimal")] Value::Decimal(Some(v)) => write!(buf, "{v}")?, #[cfg(feature = "with-bigdecimal")] diff --git a/src/value.rs b/src/value.rs index 173143c3f..bd1a31129 100644 --- a/src/value.rs +++ b/src/value.rs @@ -162,13 +162,6 @@ pub enum ArrayType { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] JiffTimestamp, - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) - )] - JiffZoned, - #[cfg(feature = "with-uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] Uuid, @@ -301,13 +294,6 @@ pub enum Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] JiffTimestamp(Option>), - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) - )] - JiffZoned(Option>), - #[cfg(feature = "with-uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] Uuid(Option), @@ -471,13 +457,6 @@ impl Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] Self::JiffTimestamp(_) => Self::JiffTimestamp(None), - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) - )] - Self::JiffZoned(_) => Self::JiffZoned(None), - #[cfg(feature = "with-uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] Self::Uuid(_) => Self::Uuid(None), @@ -614,17 +593,6 @@ impl Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] Self::JiffTimestamp(_) => Self::JiffTimestamp(Some(Timestamp::UNIX_EPOCH.into())), - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) - )] - Self::JiffZoned(_) => Self::JiffZoned(Some( - Timestamp::UNIX_EPOCH - .to_zoned(jiff::tz::TimeZone::UTC) - .into(), - )), - #[cfg(feature = "with-uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] Self::Uuid(_) => Self::Uuid(Some(Default::default())), @@ -756,13 +724,6 @@ impl Value { #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] Self::JiffTimestamp(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())), - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))) - )] - Self::JiffZoned(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())), - #[cfg(feature = "with-uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] Self::Uuid(v) => array_type_of(v), diff --git a/src/value/hashable_value.rs b/src/value/hashable_value.rs index cb78b3ad3..49abad60a 100644 --- a/src/value/hashable_value.rs +++ b/src/value/hashable_value.rs @@ -57,8 +57,6 @@ impl PartialEq for Value { (Self::JiffDateTime(l), Self::JiffDateTime(r)) => l == r, #[cfg(feature = "with-jiff")] (Self::JiffTimestamp(l), Self::JiffTimestamp(r)) => l == r, - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - (Self::JiffZoned(l), Self::JiffZoned(r)) => l == r, #[cfg(feature = "with-uuid")] (Self::Uuid(l), Self::Uuid(r)) => l == r, @@ -143,8 +141,6 @@ impl Hash for Value { Value::JiffDateTime(datetime) => datetime.hash(state), #[cfg(feature = "with-jiff")] Value::JiffTimestamp(timestamp) => timestamp.hash(state), - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - Value::JiffZoned(zoned) => zoned.hash(state), #[cfg(feature = "with-uuid")] Value::Uuid(uuid) => uuid.hash(state), diff --git a/src/value/postgres_array.rs b/src/value/postgres_array.rs index bf86459de..713dc04ae 100644 --- a/src/value/postgres_array.rs +++ b/src/value/postgres_array.rs @@ -59,9 +59,6 @@ impl NotU8 for jiff::civil::DateTime {} #[cfg(feature = "with-jiff")] impl NotU8 for jiff::Timestamp {} -#[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] -impl NotU8 for jiff::Zoned {} - #[cfg(feature = "with-rust_decimal")] impl NotU8 for Decimal {} diff --git a/src/value/prelude.rs b/src/value/prelude.rs index 660c004fc..97b044d95 100644 --- a/src/value/prelude.rs +++ b/src/value/prelude.rs @@ -8,7 +8,7 @@ pub use chrono::{self, DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, N pub use time::{self, OffsetDateTime, PrimitiveDateTime}; #[cfg(feature = "with-jiff")] -pub use jiff::{self, Timestamp, Zoned}; +pub use jiff::{self, Timestamp}; #[cfg(feature = "with-rust_decimal")] pub use rust_decimal::{self, Decimal}; diff --git a/src/value/with_jiff.rs b/src/value/with_jiff.rs index 6f2bf3bb4..73814bd39 100644 --- a/src/value/with_jiff.rs +++ b/src/value/with_jiff.rs @@ -1,28 +1,20 @@ use super::*; -#[cfg(feature = "unimplemented-jiff-zoned")] -use jiff::Zoned; use jiff::{Timestamp, civil}; type_to_value!(civil::Date, JiffDate, Date); type_to_value!(civil::Time, JiffTime, Time); type_to_box_value!(civil::DateTime, JiffDateTime, DateTime); type_to_box_value!(Timestamp, JiffTimestamp, TimestampWithTimeZone); -#[cfg(feature = "unimplemented-jiff-zoned")] -type_to_box_value!(Zoned, JiffZoned, TimestampWithTimeZone); impl DateLikeValue for civil::Date {} impl TimeLikeValue for civil::Time {} impl DateTimeLikeValue for civil::DateTime {} impl DateTimeLikeValue for Timestamp {} -#[cfg(feature = "unimplemented-jiff-zoned")] -impl DateTimeLikeValue for Zoned {} impl DateLikeValueNullable for Option {} impl TimeLikeValueNullable for Option {} impl DateTimeLikeValueNullable for Option {} impl DateTimeLikeValueNullable for Option {} -#[cfg(feature = "unimplemented-jiff-zoned")] -impl DateTimeLikeValueNullable for Option {} impl Value { #[inline] @@ -44,12 +36,6 @@ impl Value { pub fn jiff_timestamp>>(v: T) -> Value { Value::JiffTimestamp(v.into().map(Into::into)) } - - #[cfg(feature = "unimplemented-jiff-zoned")] - #[inline] - pub fn jiff_zoned>>(v: T) -> Value { - Value::JiffZoned(v.into().map(Into::into)) - } } impl Value { @@ -69,11 +55,6 @@ impl Value { matches!(self, Self::JiffTimestamp(_)) } - #[cfg(feature = "unimplemented-jiff-zoned")] - pub fn is_jiff_zoned(&self) -> bool { - matches!(self, Self::JiffZoned(_)) - } - pub fn as_ref_jiff_date(&self) -> Option<&civil::Date> { match self { Self::JiffDate(v) => v.as_ref(), @@ -101,14 +82,6 @@ impl Value { _ => panic!("not Value::JiffTimestamp"), } } - - #[cfg(feature = "unimplemented-jiff-zoned")] - pub fn as_ref_jiff_zoned(&self) -> Option<&Zoned> { - match self { - Self::JiffZoned(v) => v.as_deref(), - _ => panic!("not Value::JiffZoned"), - } - } } impl Value { @@ -119,8 +92,6 @@ impl Value { Self::JiffTime(v) => v.as_ref().map(|v| v.to_string()), Self::JiffDateTime(v) => v.as_ref().map(|v| v.to_string()), Self::JiffTimestamp(v) => v.as_ref().map(|v| v.to_string()), - #[cfg(feature = "unimplemented-jiff-zoned")] - Self::JiffZoned(v) => v.as_ref().map(|v| v.to_string()), _ => panic!("not jiff Value"), } } diff --git a/src/value/with_json.rs b/src/value/with_json.rs index 9bfcab7d3..d252becf9 100644 --- a/src/value/with_json.rs +++ b/src/value/with_json.rs @@ -95,8 +95,6 @@ pub fn sea_value_to_json_value(value: &Value) -> Json { Value::JiffDateTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), #[cfg(feature = "with-jiff")] Value::JiffTimestamp(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(all(feature = "with-jiff", feature = "unimplemented-jiff-zoned"))] - Value::JiffZoned(_) => CommonSqlQueryBuilder.value_to_string(value).into(), #[cfg(feature = "with-rust_decimal")] Value::Decimal(Some(v)) => { use rust_decimal::prelude::ToPrimitive; From 418eba54e2832bf4e77a78fac0e953a14be255c8 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Mon, 23 Mar 2026 05:09:04 +0800 Subject: [PATCH 10/11] Update rusqlite --- sea-query-rusqlite/Cargo.toml | 3 ++- sea-query-rusqlite/src/lib.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sea-query-rusqlite/Cargo.toml b/sea-query-rusqlite/Cargo.toml index 462d8911f..9ffbfa784 100644 --- a/sea-query-rusqlite/Cargo.toml +++ b/sea-query-rusqlite/Cargo.toml @@ -27,8 +27,9 @@ with-rust_decimal = ["sea-query/with-rust_decimal"] with-bigdecimal = ["sea-query/with-bigdecimal"] with-uuid = ["rusqlite/uuid", "sea-query/with-uuid"] with-time = ["rusqlite/time", "sea-query/with-time"] +with-jiff = ["rusqlite/jiff", "sea-query/with-jiff"] with-ipnetwork = ["sea-query/with-ipnetwork"] with-mac_address = ["sea-query/with-mac_address"] postgres-array = ["sea-query/postgres-array"] postgres-vector = ["sea-query/postgres-vector"] -sea-orm = [] \ No newline at end of file +sea-orm = [] diff --git a/sea-query-rusqlite/src/lib.rs b/sea-query-rusqlite/src/lib.rs index 69cf4aaf2..2b7ab3194 100644 --- a/sea-query-rusqlite/src/lib.rs +++ b/sea-query-rusqlite/src/lib.rs @@ -109,6 +109,14 @@ impl ToSql for RusqliteValue { Value::TimeDateTime(v) => v.to_sql(), #[cfg(feature = "with-time")] Value::TimeDateTimeWithTimeZone(v) => v.to_sql(), + #[cfg(feature = "with-jiff")] + Value::JiffDate(v) => v.to_sql(), + #[cfg(feature = "with-jiff")] + Value::JiffTime(v) => v.to_sql(), + #[cfg(feature = "with-jiff")] + Value::JiffDateTime(v) => v.to_sql(), + #[cfg(feature = "with-jiff")] + Value::JiffTimestamp(v) => v.to_sql(), #[cfg(feature = "with-uuid")] Value::Uuid(v) => v.to_sql(), #[cfg(feature = "with-json")] From 9524c7784789531190bab33b13cd1e13c0a08fad Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Mon, 23 Mar 2026 05:55:44 +0800 Subject: [PATCH 11/11] Fix type mapping --- src/value/with_jiff.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value/with_jiff.rs b/src/value/with_jiff.rs index 73814bd39..f3d1dca9e 100644 --- a/src/value/with_jiff.rs +++ b/src/value/with_jiff.rs @@ -3,7 +3,7 @@ use jiff::{Timestamp, civil}; type_to_value!(civil::Date, JiffDate, Date); type_to_value!(civil::Time, JiffTime, Time); -type_to_box_value!(civil::DateTime, JiffDateTime, DateTime); +type_to_box_value!(civil::DateTime, JiffDateTime, Timestamp); type_to_box_value!(Timestamp, JiffTimestamp, TimestampWithTimeZone); impl DateLikeValue for civil::Date {}