Ruby time zone conversions and always showing local time to the user - ruby-on-rails

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?

Related

How to handle timezones in Rails when using fixed time ranges

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.

Postgresql date difference in table and on web

I have weird situation with my PostgreSQL db date value.
On my web site I have calendar for selecting a date and when I select some future date like "2018-09-23" in PostgreSQL table column it is saved as "2018-09-22 22:00:00"?
Obviously I am missing something. On web site all the time it shows okay time "2018-09-23" but at the table it is minus one day as you see above. Why?
Rails stores DateTime fields in UTC, but without marking their time zone as UTC. This corresponds to the timestamp without time zone type in postgres. So if your time zone is +2, it'll store the time as UTC (+0).
In Rails, Time.zone will return the current local timezone (you can add logic to change this by user, for example). When persisting a datetime, Rails will automatically convert the current Time.zone to UTC. However, it doesn't use the Postgres type that actually includes the time zone data, so it relies on convention to convert back and forth to the user's time zone.
If you really only care about the date, use the date type in your migration instead of Timestamp or DateTime.
Times and dates have a lot of subtle quirks and the "right" behavior depends on your use case. In some applications, you need to deal with "local" time when considering date transitions, and sometimes you need to finesse your application or database logic to think in terms of local time and sometimes you care about UTC time.

Supporting Timezones in Rails

I've got an application that allows users within the same company to create job records, view, edit, and search these jobs. The users are spread out all over the United States. A user in California may need to update a job for a user in New York and visa versa.
I read through an article that suggested setting Time.zone in the controller with a before action, but if I do this, I assume that Rails would then save that record in the current time zone for like the California user. Then, if the New York user updates the same record then updated_at time would then be in a different time zone than the created_at attribute. Ideally, I see all of my records having a UTC time and then when a given user accesses/creates records, the time is displayed to them in their set timezone but saved in the database as UTC. I'm not sure how to do this or if it's even the correct approach. Can anyone provide some guidance?
Thanks in advance.
#Cannon Moyer if you see table structure of any table. updated_at and created_at columns store timestamp without timezone. When displaying, These values are automatically converted with timezone as application's timezone(UTC or IST) is set. so don't need to worry about time when a given user accesses/creates records.
For your case as #iGian suggested this gem is useful for you. It sets the Rails timezone to the browser's configured timezone for each request.

Rails seems to be creating objects in wrong time zone for created_at, updated_at values

I just created a new object using a form and its updated_at and created_at values are
created_at: "2013-04-25 03:22:19", updated_at: "2013-04-25 03:22:19",
However that is ~7 hours ahead of where I am (PST).
Time.now.to_s
=> "2013-04-24 20:23:12 -0700"
How can I make sure the time zone is consistent with wherever a user is creating it from?
Your created_at and updated_at values are written from your server to the database, so they will always be in your server's or application's time zone. By the time the server processes the form data and saves to your database, it has no knowledge of the browser's time zone.
You can set a Rails time zone using config.time_zone, as #lulalala suggested.
It sounds like you're interested in displaying times to your users in their native time zones. You have two options for this:
Ask your user for a time zone on signup (or assign a default one and allow users to edit it)
Detect time zone using javascript and report back to your server (http://josephscott.org/archives/2009/08/detecting-client-side-time-zone-offset-via-javascript/) - not guaranteed to be correct
Once you have the time zone saved as a user attribute, you can display your times to your users like this:
Model.created_at.in_time_zone(#current_user.time_zone)
I check irb timezone with the command > Time.now.to_s and the time was correct, but the app save it wrong by 3 hours. So..
The solution for me was add config.time_zone = "Santiago" to config/environments/development.rbfile. The time zone list is here, and my entry was
"Santiago" => "America/Santiago"

Time.now.midnight's susceptibility to time zones

I have some daily analytics-style records that track usage on my site, and they work as follows:
When an 'event' occurs, the site looks for a record that was created at Time.now.midnight.
If such a record is not found, a new one is created, and created_at is set to Time.now.midnight.
Here's the question - does Time.now.midnight get recorded differently depending upon the client's time zone? If so, am I correct in assuming that the above very simple system will break down?
How can I fix it so all analytics records, irrespective of the time zone of the user who triggered their creation, get pegged to one time?
Note: I imagine that setting the created_at field to a Date instead of Datetime might have been better in this scenario. Let's assume we're stuck with datetime for this question.
Time.now does not get recorded differently based on the clients timezone.
Time.now returns a new time using the system timezone (aka the server)
To use a client specific timezone you have to have a user select their zone and use Time.current or Time.zone.now (which do the same thing)
created_at is usually pinned to UTC, so you shouldn't have any issues their either.
(to change this you need to change Rails.root/config/application.rb)
config.time_zone = "whatever you want the ActiveRecord default to be"

Resources