Replies: 4 comments 6 replies
-
I like the idea of a default time format, but I think it'll need to be something that can be specified at the field level somehow on the model as well? |
Beta Was this translation helpful? Give feedback.
-
I think the root cause of your issue here isn't a time formatting problem, but a drift between what the computer that's running your server think is "now" and what the database server thinks is "now". As you said, when everything is local it works fine and when everything is UTC it works fine. In the case of the Postgres docker image, they've run into this issue before and provide some environment variables to solve the sync issue. Is there some room in the boot-time infrastructure to do some checking on the two systems and ensure they're in sync? That might be a nice warning to emit. |
Beta Was this translation helpful? Give feedback.
-
This comment from @jwoertink on #732 seems really important to me:
That's a really critical gotcha! In the rare event where the server and database exist in different timezones this would translate to "mysterious behavior" quickly. |
Beta Was this translation helpful? Give feedback.
-
Just some notes on this, since it popped up in discord. Postgres timestamptz internally stores the date as UTC. Always. No way around this. What changes is the "internal" timezone that is used at database or session level.
To show an example: davide@Davides-MBP ~ % psql postgres -c "create database test_db" #create a test database
CREATE DATABASE
davide@Davides-MBP ~ % psql test_db -c "select now();" #now() returns a timestamptz (my host tz is Eu/Zurich)
now
-------------------------------
2021-11-05 16:26:30.848786+01
(1 row)
davide@Davides-MBP ~ % psql test_db -c "set time zone 'UTC'; select now();" # forcing a new timezone for the session
now
-------------------------------
2021-11-05 15:26:57.339187+00
(1 row)
davide@Davides-MBP ~ % psql test_db -c "select now();" # after the session, the default tz stays the same as the original one
now
-------------------------------
2021-11-05 16:27:03.545999+01
(1 row)
davide@Davides-MBP ~ % psql test_db -c "alter database test_db set time zone 'Indian/Christmas'" # now, forcing the default tz to something else
ALTER DATABASE
davide@Davides-MBP ~ % psql test_db -c "select now();" # and the timestamp has a different timezone across all sessions
now
-------------------------------
2021-11-05 22:27:58.443664+07
(1 row) Then, it really depends on your use case, whether it is a "I should support n-different timezones" (so potentially one for each session) or a "all my stuff will be seen in a single timezone" (like everything in CEST). (in which case I usually do a alter database) |
Beta Was this translation helpful? Give feedback.
-
This is sort of a big one, and could end up being a breaking change, so I'll try to explain the issue and possible solution.
This issue I first ran in to here was caused because I wrote a spec that inserted several records in to my DB, then sorted them by their
created_at
, and didn't get consistent results that I expected. This was because they were all inserted within the same second, so they all had an identicalcreated_at
. Knowing the exact order is pretty important based on time here.On my first investigation, I noticed we don't store the milliseconds of the time. Avram just takes the time object from Crystal and calls to_s on it. So whatever format that returns is what we shove in to the DB. Changing the format here to include milliseconds fixed my issue.
Next I ran in to an issue where my specs passed locally on my Linux machine, but on my Mac they failed. I also noticed they failed on the CI. Turned out that a default postgres install takes the timezone of the host machine, and sets the timezone for that DB. My Mac used Docker for postgres, and the CI server is set to UTC, but my local Linux machine uses PDT.
This is normally ok since if you store everything locally in your local timezone, then it's all relative. Your user's
created_at
is in PDT, and it doesn't really matter. My issue was that I need to pull out a specific set of records within a specific time frame, and the difference between PDT and UTC is 7 hours. I was converting times to UTC in Crystal, and my local Linux converted it to PDT, but my Mac and CI kept the time as is giving me the wrong result.To fix this, I added a migration that ensures the DB is always UTC. Then running locally at my home, or another dev that lives 3 hours ahead, it doesn't matter. We always use the same timezone, and it's all good. However, this led to another issue... Running queries that used timespan like
30.days.ago
always usesTime.local
. This is based on your host machine where Crystal runs, not where Postgres is at. If my DB was in PDT, and my host machine is PDT, then30.days.ago
is all the same. Now that my host machine is PDT, and postgres is UTC, it's not the same.We can now use
30.days.ago.to_utc
to get around this, but we have to ALWAYS remember to add theto_utc
. Small issue, but a bit annoying.Ok, so to recap, at this point I've
to_utc
calls all over the place to get proper queriesWhat's the issue now? Well, with this new format, parsing the column when pulling the value out of the DB, Avram iterates over all possible formats. If the format fails, then it moves on to the next format. What ends up happening is the first format that passes is actually
%Y-%M-%d
😬 yeah... just the date... no time. Ok, fine, we can fix this by setting our own custom format. Then Avram will use this custom format each time.These all solve the issues for my app, but in order to do this, it required me patching Avram.
The proposal
My thought is, what if Avram had a concept of a single default format, as well as a default timezone?
What these would do is replace the logic of iterating over several different time formats. Maybe we get a performance boost here? It would also allow you to set a DB to some different timezone if you wanted. I've had an app in the past that required the timezone to always be local, and the app wasn't centralized. It's a rare case, but not unheard of. Right now, you're forced to always use UTC
Some questions to answer though is
date
column, andtime
column anddatetime
?Time.local
that it is globally a specific timezone?Beta Was this translation helpful? Give feedback.
All reactions