Row based Time Zones in Rails 2.3.4 - ruby-on-rails

What is the best way to handle the following situation:
We have "admins" who create "events". These events can be nationwide, ie: different time zones. As of now there is a select menu with the current list of US time zones. There is a corresponding column in the database table, for the time zone selected. This is so the admin can select 5PM Eastern, 5PM Pacific, etc. Instead of trying to figure out the GMT equivalent.
The application is using UTC as per the environment.rb file, as the default time-zone.
If an admin selects "Eastern Time (US & Canada)", the date stamp selected is still saved as UTC (or the app default). I need to perform queries where events don't show up before the time selected above including the time zone.
Since the events need their own time zone, how should the data be saved?
I was originally thinking I need to trick Rails before it saves the value, so it saves UTC offset for the time zone.
This would be handled in before_save and after_find model methods. But it doesn't seem to work, or rather I am missing something..

You're missing a simple fact. Time is universal. 1:00pm in UTC is 1:00pm in UTC anywhere. You don't even need to store a time_zone with every event. You just need to let admins select it for easier time input. As far as backend, it's always in UTC. You can certainly input time from different timezone, and output into different timezone, but it's still the same "1:00pm in London" no matter how you twist it. So when a user is looking at events, you can simply do event_date < Time.now.utc (all past events).
However, if the observing user is actually in a different timezone from the events, and you want to display event dates in the user's home timezone, then you can let user select their home timezone, and store it in users table. Then in before_filter on your ApplicationController you can just do:
class ApplicationController < ActionController::Base
before_filter :set_time_zone
private
def set_time_zone
Time.zone = current_user.time_zone if current_user
end
end
Then every Time value that comes from database (such as event.date) will be automatically converted in the user's home time zone.
Or maybe I misunderstood the question. : ) Let me know.

Related

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)

Finding records across time zones using active record

I'm developing an app where a user can request that an email be sent to them at a specific time every day in their timezone.
For example User A lives in London and schedules an email at 2pm every day London time and User B lives in New York and schedules an email at 2pm New York time.
I'm wondering how I should store my schedule timestamps in my database and what supporting information I need to store along side this to support the fact that all times depend on a specific timezone.
I'm thinking that storing the timestamps as utc with a separate column for the timezone is the way to go but I'm unsure of how to query the database to return all scheduled emails at a specific time without having to perform one query per timezone.
Yes, store the data in UTC - rails will do this by default; put a time_zone column on your User model
Here is a post I wrote for timezones in rails which you might find helpful: http://jessehouse.com/blog/2013/11/15/working-with-timezones-and-ruby-on-rails/
This might be overly complicated, but I am thinking if you had a background job running (delayed_job, sidekiq, etc) it would check each timezone/email schedule - something like this (You definitely want time_zone on your User, maybe on the schedule as well?)
# loop through all timezones
ActiveSupport::TimeZone.zones_map(&:name).each do |time_zone|
# get a list of any saved schedules in this timezone
scheduled_in_zone = EmailSchedule.where(time_zone: time_zone)
# execute block in that timezone
Time.use_zone(time_zone) do
# Time.current is timezone aware (same as Time.zone.now)
now = Time.current
scheduled_in_zone.each do |scheduled|
if scheduled.deliver_at <= now
scheduled.deliver_it!
end
end
end
end
I also recommend checking out: http://railscasts.com/episodes/106-time-zones-revised

Managing multiple users time zones in ruby on rails

I want to save an event in a default time zone i.e. Whenever an user submits an event it will get convert to the default time zone and will be save in the database.
Whenever user requests for an event, the system will find that users time zone and convert the date (from the default format) in that user time zone and display it to the user.
I'm having difficult time where to start from i saw many notes and documents but couldn't figure it out the complete process of doing it.
I saw this code but couldn't understand how to use it :
before_filter :set_time_zone
def set_time_zone
Time.zone = current_user.time_zone if current_user
end
The default time zone in the database is UTC.
There are 2 ways you can do it:
Allow their user to store their time zone in the database
Grab their time zone from their IP
For option 1, Rails has very good support for time zones. The #all method will allow you to create a dropdown for them to choose from. Then save it in the database with the user record.
Option 2 is less work for the user, but is also less accurate. There are a few services that convert IP's to Time Zones.
To display the time in a given time zone, use Time#in_time_zone or you can set the Time.zone as above and it should display properly.

Rails 3.1 time zone confused?

Here is my current situation:
I have a user class that has an attribute time zone.
When the user creates a Lecture with start_time (3pm) and end_time (5pm), I want to ensure that in the database the start_time and end_time are actually 3pm and 5pm, in the user's time zone.
In my application controller I'm doing the following:
def set_timezone
if current_user
Time.zone = current_user.time_zone or "Eastern Time (US & Canada)"
end
end
When the above lecture is saved to the database it seems to add 4 hours. Is it converting it back to UTC? The odd thing is that when I display the time in the view it is correct (I'm guessing it's converted from the UTC time back to the EST time).
I want it so that that if a user selects 4pm it is saved in the database as 4pm AND when I display that time in the view it is 4pm. What am I currently doing wrong?
EDIT: it appears that rails also converts all times to UTC when storing them in the database. Which is ok until I do a query that involves time (I'll have to manually convert it to UTC).
MySQL also has a time zone.
Notably (emphasis mine):
The current session time zone setting affects display and storage of time values that are zone-sensitive. This includes the values displayed by functions such as NOW() or CURTIME(), and values stored in and retrieved from TIMESTAMP columns. Values for TIMESTAMP columns are converted from the current time zone to UTC for storage, and from UTC to the current time zone for retrieval.
Whether or not it's worth doing anything about it... not sure. I'm skeptical.

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