How to handle timezones in Rails when using fixed time ranges - ruby-on-rails

I feel like I'm going round the houses on this one.
In summary, I have a Location model which has opening hours, modelled as opens_at and closes_at in the DB.
All times are stored as UTC in the database.
If I create a location in New York with opens_at set at 08:30 in my form and it gets saved to the DB as 08:30 UTC, why would I need to worry about timezones? I don't want to display the time in local time because the user would then see opening hours of 03:30.
Am I overthinking this? Do I only need to worry about timezones if I have users in multiple countries attending the same event at the same time?
As this is about physical locations, it feels to me that it wouldn't be needed.

If I create a location in New York with opens_at set at 08:30 in my form and it gets saved to the DB as 08:30 UTC, why would I need to worry about timezones?
Because 08:30 in New York is not 08:30 UTC. It's not even the same time for all dates.
It's 13:30 UTC when New York is observing Eastern Standard Time (EST, UTC-5)
It's 12:30 UTC when New York is observing Eastern Daylight Time (EDT, UTC-4)
So, if the location opens at the same time every day, then you must store it in local time. Otherwise you'll be off by an hour one way or the other following the next DST transition.
This can be an issue even for time zones that don't use DST, and even for non-recurring events, because the various governments of the world control the time zones within their borders. They can change whether to start observing DST or stop observing it, the dates and times that DST transitions occur, or their standard offset from UTC. Some give adequate notice when they make such changes, and others do not. There's always some non-zero chance that a given time zone might change its behavior between when you initially recorded the event and when the event takes place. If you recorded using local time, all you need to do is update the time zone data on your systems (which often happens automatically). However, if you recorded the event using UTC then you could be off when converting back after such a change.
In general, future events should always be stored in terms that the event is described - which is almost always in local time of some particular time zone. Store the IANA time zone ID (e.g., "America/New_York") with the location or event so that you can convert when you need to, but converting to UTC prematurely can lead to losing sight of the original information.
Save the "Always UTC" mindset for past or present events, such as timestamping a transaction.

Yes, I actually have run into this. If the user is only going to see the time in the local time zone, then it is not really necessary to save in UTC with time zone. You can save it in local time without time zone.
Reference: Postgres Time without Time Zone
Some developers will say that you should save it in UTC with the time zone, but I can see two arguments against it:
If you don't need it now and you may never need it.
You may not have access to the Time Zone now or may need extra code/complexity to implement and validate.
You can probably add it later if you know the time zone or lat/lon or address.

Related

What is the opposite of an AoE expiry?

I'm speccing an application that displays time periods to the user. The goal is to present periods in a simple view (no time, no timezones) and detailed view (date and time, with timezone data). The simple view should be unambiguous, in other words the user can glance at it and their assumptions about what they see are correct (they are valid in the local timezone).
For the end of the global period, displaying the date in the AoE timezone [1] will solve this problem. For example, a submission deadline might display as 2018-04-03 (actually 2018-04-03 23:59:59 AoE). This means submissions are accepted as long as it is April 3 somewhere on the planet.
But I also want to indicate that start of a global period. For example, if submissions open on April 2 2018 00:01, they are accepted as soon as it is April 2 somewhere on the planet. (This would currently be at UTC+14, matching the Line Islands.)
I can't see a way to use AoE to derive a global start time. Is there an equivalent to AoE (a standardized semantic timezone) that tracks the global start time?
Notes:
Hardcoding UTC-12 and UTC+14 is the simple answer for the modern day. But I'm looking for semantic timezones that would be updated if the values changed (and not reference non-existent historical datetimes).
I thought I'd seen Etc/AoE in the tz database but this is not the case.
References:
AoE
UTC-12:00
UTC+14:00
[1] The Anywhere on Earth (AoE) timezone represents the moment a datetime expires "anywhere on Earth". It currently matches time at Howland Island (UTC-12). If a UTC-13 timezone were invented, it would be updated to track that.
As far as I could understand, AoE is not a timezone as defined by IANA (AFAIK, a list of all offsets from some geographic region during history).
It's more like a "concept", an idea of a specific date being valid in any place on earth. As you said, this notion of "being valid" will change if more timezones are created or removed.
I don't even know if date/time API's can properly handle AoE automatically - maybe I should study more. But my conclusion is that the only way to achieve your goal is to check manually:
you could check all available timezones and see if the date is valid there, comparing to the current date/time at that zone
you could configure the UTC+14 as the offset to be compared, and make some scheduled job (daily/weekly/every-time-IANA-publishes-a-new-version?) to check all zones and set the correct one (with the biggest offset?). You must also take care if this zone has Daylight Saving changes, because the offset will change as well (and what to do with overlaps, when clocks shift 1 hour back and a local time may exist twice?)

Guess timezone name for custom date

I know that I can get browser timezone name: moment.tz.guess()
How can I guess timezone for custom date? e.g. moment("2020-12-30T14:17:40+11:00").tz.guess() ?
You can't. There are 9 different location-based zones that use +11:00 year round, and 5 more that use it for daylight saving time.
You might be able to eliminate a few if the date and time put it into a time zone where daylight time is not in effect and the standard time doesn't match, but from there, the best you could do would be to pick one at random.
See "time zone != offset" in the timezone tag wiki.
Moment-timezone can guess the browser's time zone because it can query the offset for multiple different timestamps, algorithmically reducing the possibilities. Even then, there are often several that could match, and thus the most likely one is picked (based on population statistics). In the end, it's still a guess.

Ruby time zone conversions and always showing local time to the user

I have a system of cities and events that happen in those cities. When a user creates an event, it is associated with a city, and therefore a time zone. I'm confused about Ruby's various implementations (Date, Time, DateTime) and how to handle the saving and displaying of event times relative to a city's time zone.
I have a datetime column in my database. My HTML form has a field for city (and therefore that city's time zone) and I use datetime_select() to give the user fields to enter the event's date and time. The idea is that if the user has selected "Seattle", and then a time of 7pm on April 1, the user should not have to worry about time zones at all, and the assumption is that they are setting it for 7pm Seattle time. I want to either save this datetime as UTC (after making the adjustment from Seattle time to UTC) or apply the Seattle time zone to it without altering the specified hour. Since I am using mass assignment when I save my form data to the database, I'm guessing I need to alter the data (adjusting for time zone) before saving, otherwise, it will assume that this 7pm time is already UTC, which of course it isn't.
When I display this information from the database, it will be in the context of a time zone, so when displaying this event, the system knows it is in Seattle and will therefore show 7pm (as the original user who created the event intended).
How should I handle this? What implementation should I use (Date, Time, DateTime), and what conversions should be done on saving and loading the data from the database so users always see local times?

ActiveRecord's weird timezone handling when Daylight Saving Time changes

I have an application providing agenda functionalities.
Disclaimer: I really love Rails and AR's functionalities, this is just meant to ask how can I exploit it functionalities better the next time.
Since it has to be used by a european team it has been developed using +0200 timing informations. I know that the timezone stuff is handled by AR and the base data is always stored in UTC.
The issue I get (but I already solved, just wanted to get some feedback to think it better the next time) is that when I store a new appointment I also enforce "+0200" at the end of the DateTime.parse call (es. DateTime.parse("#{day} #{hour} +0200") and then I set it as record attribute. Of course 10:00 gets stored as 08:00 inside the data layer, and that's fine.
Then when I retrieve that data using the configured AR timezone (Rome for instance) using something like #appointment.start_at it correctly gets converted back when we are under the CEST daylight time. Issues start happening when start_at are set to be CET (+0100) dates (but I always store it inside the database enforcing CEST +0200).
Inside the database start_at is always the same, so the UTC representation is always 08:00 when saving an appointment with 10:00 but the AR conversion uses the wrong daylight conversion (CET).
I know I could skip this by simply working all on UTC so I don't have to carry out any consideration about daylight savings times, but it seems a bit messy that AR itself is not able to remember somehow that I stored it while CEST is running.
When performing queries I do the same as when storing, so I append "+0200" to all DateTimes, and all works back again, but when displaying I need to check whether the TimeZone is "CET" or "CEST" and add a +1.hour to CET (otherwise it results the appointment has been signed 1 hour before than the intended hour).
Is it right I should handle this by myself?
Shouldn't be AR able to perform this kind of check since it wraps the data-layer?
How should I design an application the next time to avoid this kind of issue while still using local timezones? (as I said using UTC may also not be applicable by some requirements)
I thought I should change "+0200" with "in_time_zone" the next time, but I also allow users to write by hand an hour to search, and of course this will always be the same time either it is "CET" or "CEST" and of course I cannot enforce users to write following the UTC format.
Thank you,

Rails timezone and Daylight saving time

for a while I´m trying to understand how this timezone times will work, and I had a question:
Today in my country, we are in Daylight saving time (GMT-2).
So the user of my application enter a time, like 11:00AM and post the form.
Far as I know, rails will convert this date to UTC and save in the database (mysql in my case), like: 01:00PM UTC.
When I recover that record, I had to convert to local time to display. Ok?
My question is, lets suppose that this date/time represents a date/time in future, when my country gets out from summer time (GMT-3). Rails will save 01:00PM UTC? Today, in Daylight saving time, how will be the local time? And in the future, how will be this local time?
Basically, I always need to display to user 11:00AM.
thanks.
There are several places where timezone can come into play: the operating system (or probably user account) default setting, the database server, Rails environment.rb.
The key is to make sure all dates are stored with UTC time zone, then displayed in whatever your local timezone is. It sounds like you're doing that.
So your question seems to boil down to "if it's Daylight time, I want to offset by -3 hours, else offset by -2 hours". The Rails time extensions let you determine your current offset like Time.zone.now.utc_offset, and Time#dst? tells you if it's Daylight Savings Time with those two you can conditionally subtract the extra hour (3600 hundred seconds).
7 months after you asked, but perhaps skip_time_zone_conversion_for_attributes= will help - it tells AcitveRecord not to convert timezones on storage or retrieval. See ActiveRecord Timestamp which shows the example:
class Topic < ActiveRecord::Base
self.skip_time_zone_conversion_for_attributes = [:written_on]
end

Resources