-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add FromSql/ToSql<Timestamp> to Pg backend for OffsetDateTime #3973
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,11 @@ use crate::sql_types::{Date, Time, Timestamp, Timestamptz}; | |
// Postgres timestamps start from January 1st 2000. | ||
const PG_EPOCH: PrimitiveDateTime = datetime!(2000-1-1 0:00:00); | ||
|
||
fn to_primitive_datetime(dt: OffsetDateTime) -> PrimitiveDateTime { | ||
let dt = dt.to_offset(UtcOffset::UTC); | ||
PrimitiveDateTime::new(dt.date(), dt.time()) | ||
} | ||
|
||
#[cfg(all(feature = "time", feature = "postgres_backend"))] | ||
impl FromSql<Timestamp, Pg> for PrimitiveDateTime { | ||
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> { | ||
|
@@ -44,6 +49,23 @@ impl ToSql<Timestamp, Pg> for PrimitiveDateTime { | |
} | ||
} | ||
|
||
// Delegate offset datetimes in terms of UTC primitive datetimes; this stores everything in the DB as UTC | ||
#[cfg(all(feature = "time", feature = "postgres_backend"))] | ||
Ten0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
impl ToSql<Timestamp, Pg> for OffsetDateTime { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
However, implementing => I would prefer that we do not do this, and let users write NB: I couldn't find an actual There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While strictly true, this winds up being incredibly verbose in practice. In particular where users of When deriving In the other direction when storing rows, you wind up having to write wrapper fns to manually convert There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case where you don't control your database schema, I think you have two reasonable options:
As outlined by weiznich the reason is more likely that MySQL has a different meaning than Postgres for the So it looks like on the MySQL side, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is interesting. I didn't realize the Timestamp behavior was that different, but I guess it does make sense here.
This is a really clever approach. I was able to make this switch in my own code, and things generally worked as you've described. The only place I ran into issues was with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC |
||
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { | ||
let prim = to_primitive_datetime(*self); | ||
<PrimitiveDateTime as ToSql<Timestamp, Pg>>::to_sql(&prim, &mut out.reborrow()) | ||
} | ||
} | ||
|
||
#[cfg(all(feature = "time", feature = "postgres_backend"))] | ||
impl FromSql<Timestamp, Pg> for OffsetDateTime { | ||
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> { | ||
let prim = <PrimitiveDateTime as FromSql<Timestamp, Pg>>::from_sql(bytes)?; | ||
Ok(prim.assume_utc()) | ||
} | ||
} | ||
|
||
#[cfg(all(feature = "time", feature = "postgres_backend"))] | ||
impl FromSql<Timestamptz, Pg> for PrimitiveDateTime { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Side note: I see that we already do implement this, both here and in the |
||
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems arbitrary.
I think I would have expected the one that just drops timezone information (turns into naive) without converting to UTC, but arguably that's arbitrary as well.
In fact, it seems that there's really no non-arbitrary way to implicitly drop timezone information.
=> I would prefer that we do not do this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is arbitrary, but this is also just mirroring the current behavior of the existing
MySql
implementation.If all your timestamps are sanitized internally to UTC it's reasonably common for applications to store without timezone to save space. Since local tz obviously doesn't work, the only reasonable assumption is UTC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main difference to mysql here is that postgresql has a distinct timestamp type (
Timestamp with time zone
), which does exactly what you describe. So it seems to be a bit odd to reimplement that behavior here.Given that there is no non-arbitrary way to implicitly drop timezone information I personally would prefer to keep that step explicit for the user. I would go as far and argue that accepting the mysql impl was a mistake back then. Maybe we should put a
#[deprecated]
on that impl instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does trait implementation deprecation work now? 🤩
(Or do we just want to use the
without-deprecated
feature?)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if rustc will show the deprecation warning, but we have the
without-deprectaed
feature flag for such cases.