Time.now.midnight's susceptibility to time zones - ruby-on-rails

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"

Related

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: Query database for users with specific time value while respect individual users time zone setting

I have a background job that runs every 15 minutes and generates reminder emails. I would like to create a query that returns all users who have a specific time saved and respect how their timezone setting effects that time.
So I have a User model that stores:
:time: a users reminder time, eg 17:00:00
:string: their timezone, eg EST
So if the job runs at 17:00:00 EST, it will return users whose settings are:
reminder_time: 17:00:00, time_zone: EST
reminder_time: 13:00:00, time_zone: PST
What is the best way to build that query? Can it be done in one pass, relying on Postgres to handle the work? Do I have to stagger it, group by each time zone and doing the math for each on in Ruby?
I currently have this setup as an ActiveRecord scope that doesn't consider timezones, and I am trying to add that consideration now.
scope :receives_reminder_at, -> (time) do
ready.where(reminder_time: time)
end
When dealing with users in multiple timezones, it is normally easiest to standardize on UTC.
So store the reminder_time in UTC, this way you don't have to worry about the TZ when querying, since they will all be normalized to UTC. (assuming you are running your servers UTC. it will just work as expected). Then you just use their TZ offset in order adjust the time for their viewing.
You could use a select on User model. Something like:
User.select{|user| user.time == time_the_job_runs.in_time_zone(user.string)}
You should replace the "time_the_job_runs". It didn't got clear for me how to get it. But the in_time_zone method should be the one you're looking for to convert time based on a timezone string. Hope it helps, thanks!
Just met same problem. And found article how to make it, just how to think solve it myself. But with ready code. https://robots.thoughtbot.com/a-case-study-in-multiple-time-zones
Main idea is to:
At first you find timezones which have specific time now.
module ActiveSupport
class TimeZone
def self.current_zones(hour)
all.select { |zone|
t = Time.current.in_time_zone(zone)
t.hour == hour
}.map(&:tzinfo).map(&:name)
end
end
end
You find users with this timezone.
User.where(zone: ActiveSupport::TimeZone.current_zones(hour)).where(options)

Rails, Postgres and Timezone

I have table which have a datetime field named date. When doing a POST in order to insert a new row, the date sent from the client (browser) looks like 2015-11-20T14:30:00+10:00 which is actually a correct date and timezone.
However, inside Postgres this date has been inserted as 2015-11-20 04:30:00.000000, which as you can see, is not at all the same as above. I know the problem is related to the timezone. But I cannot seems to figure out a fix.
For information, I have configured my app timezone :
class Application < Rails::Application
config.time_zone = 'Brisbane'
end
Ideas?
2015-11-20T14:30:00+10:00 means that the local time of 14:30 is 10 hours ahead of UTC. Your database field reflects the correct UTC value of 04:30. This is often the desired behavior, especially if the value represent a timestamp - the date and time something occured (past tense).
In PostgreSQL, there are two different types of timestamp fields (reference)
The TIMESTAMP WITH TIME ZONE field accepts an input that contains a time zone offset. It then converts the value to UTC for storage. On retrieval, it uses the session's timezone setting.
The TIMESTAMP, or TIMESTAMP WITHOUT TIME ZONE simply stores the date and time given, ignoring any offset, and not converting to UTC.
Most of the time, you should indeed use TIMESTAMP WITH TIME ZONE. You should only use TIMESTAMP WITHOUT TIME ZONE if you need to retain the local date and time value, such as in scheduling of future events and calculation of business hours. And for those scenarios, it often makes more sense to split date and time into separate DATE and TIME fields.
One last thing - if you can avoid it, avoid using Rails time zones and use standard tzdb zones. "Australia/Brisbane" is the full tzdb identifier equivalent to the Rails "Brisbane" time zone. Refer to the section on Rails time zones at the bottom of the timezone tag wiki.
I found this gem to be incredibly useful and easy for correctly setting the time https://github.com/kbaum/browser-timezone-rails

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"

Resources