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

[12.x] fix: update postgres grammar date format #51363

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

calebdw
Copy link
Contributor

@calebdw calebdw commented May 9, 2024

Hello!

PostgreSQL supports time[stamp] with time zone fields with a resolution of 1 microsecond. However, the default grammar date format is Y-m-d H:i:s which truncates DateTimeInterface objects that are passed to queries.

This results in invalid queries and records being saved to / returned from the database. For example:

$datetime = Carbon::now('America/Chicago'); // 2024-05-09 15:35:55.123456-05:00
// The timestamp is shifted by 5 hours and loses the microsecond resolution!
Model::create(['datetimetz' => $datetime); // saved in DB: 2024-05-09 15:35:55+00

This PR updates the PostgresGrammar date format to prevent the lose of information:

$datetime = Carbon::now('America/Chicago'); // 2024-05-09 15:35:55.123456-05:00
// The timestamp has been properly converted to UTC for storage!
Model::create(['datetimetz' => $datetime); // saved in db: 2024-05-09 20:35:55.123456+00

Thanks!

@calebdw calebdw force-pushed the postgres_db_timezone branch 3 times, most recently from 72d3ec3 to 567bc3f Compare May 9, 2024 21:29
@taylorotwell
Copy link
Member

Can we make this change on a patch release? Seems breaking?

@taylorotwell taylorotwell marked this pull request as draft May 10, 2024 19:24
@calebdw
Copy link
Contributor Author

calebdw commented May 10, 2024

@taylorotwell,

Postgres only converts the datetime to UTC when the field contains a timezone (e.g., timestamp with time zone), otherwise it just uses the datetime as-is:

timestamp(0) with time zone = '2024-05-09 20:00:00.123456-05:00' // saved as 2024-05-10 01:00:00+00
timestamp(0)                = '2024-05-09 20:00:00.123456-05:00' // saved as 2024-05-09 20:00:00
date                        = '2024-05-09 20:00:00.123456-05:00' // saved as 2024-05-09

Thus, the conversion should only affect fields with timezones, which if people are using then they would already have to be manually converting the Carbon instances to UTC or their datetimes would be incorrect.

Also, if the timestamp precision is not 6, then Postgres will round up the microseconds (per typical rounding rules) to the appropriate precision:

timestamp(0) with time zone = '2024-05-09 20:00:00.923456-05:00' // saved as 2024-05-10 01:00:01+00
timestamp(0)                = '2024-05-09 20:00:00.923456-05:00' // saved as 2024-05-09 20:00:01

This should be fine in Production, however, this might cause some tests to fail by a second difference if they're, say, comparing a now() instance saved to the database with Carbon's ->format()/ to*String (which truncates instead of rounding). The solution is simply enough though, just call setMicroseconds(0) before persisting / converting to a string / freezing time.

I suppose to be on the safe side it would be better to merge to 12.x.

@driesvints
Copy link
Member

I also think the best is to target this at 12.x

@calebdw calebdw changed the base branch from 11.x to master May 13, 2024 14:50
@calebdw calebdw changed the title [11.x] fix: update postgres grammar date format [12.x] fix: update postgres grammar date format May 13, 2024
@calebdw
Copy link
Contributor Author

calebdw commented May 13, 2024

Updated target to 12.x

@driesvints
Copy link
Member

Make sure to mark this PR as ready if you need another review.

@calebdw calebdw marked this pull request as ready for review May 13, 2024 14:57
@taylorotwell taylorotwell marked this pull request as draft May 13, 2024 17:17
@tpetry
Copy link
Contributor

tpetry commented May 21, 2024

I encountered the issue before too but changing it by default resulted in bc breaks in edge cases. As microseconds and timezones are not used often, I am not sure whether we should complicate it. Especially as MySQL and PostgreSQL will then from now on (with default settings) behave differently.

I solved this in my extended PostgreSQL driver by using traits which is an easy thing to implement. So anyone departing from the default can change the model.

But I personally would like to not add more behavioural differences between using MySQL and PostgreSQL.

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

Successfully merging this pull request may close these issues.

None yet

4 participants