How to handle time difference in Rails application? - ruby-on-rails

In my Rails application I would like to store the last_login time of a user in a database. Then I would like to display the last_login time to user based on his local time (i.e. it should be X if user is in Canada now, and Y if user is in Australia now).
Question 1
How would you recommend to handle the time difference ? Should I store in database the last_login translated to UTC, and when displaying translate it again to the local time, or is there a better method ?
Question 2
How could I identify the local timezone for displaying the last_login ?

I think you are right about storing last_login_at in UTC (or whatever server time is). It will be stored in that time by default. Then you can convert it to user local time:
user.last_login_at.in_time_zone('Eastern Time (US & Canada)')
In Rails Api you can find more about timezones and about time with zone.
To determine user timezone you can use some GeoIP tool, for example this one (follow description there). But it's not 100% accurate detection. The most reliable way to ask user select his timezone when he signing up.
To collect all timezones you can run:
ActiveSupport::TimeZone.all.map(&:name)

Related

Accounting for daylight savings in rails webapp and iCal

Right, this is a bit confusing for me, so I'm going to try and explain from the top!
I have a rails web app. It's an internal company app and will only be used in the UK.
One of the things the app does is manage meetings.
Meetings have a date & time when they start. There's a date/time picker on the form which allows the user to pick the date & time the meeting is for. I save this date AS IS into the database. All meetings last 2 hours, so the end time is simply start + 2 hours.
Example:
2013-06-23 6:45PM in the form is stored in the db as 2013-06-23 18:45:00
2013-12-23 6.45pm in the form is stored in the db as 2013-12-23 18:45:00
Note that the first date is during Daylight Savings (BST) and the second is during GMT. I don't actually care whether it is GMT or BST: the meeting happens at that time, absolutely.
Inside the rails webapp, I simply print out the exact date & time from the DB - formatted nicely, of course!
Now, at some point I send an email to the organiser of the meeting, and the person they're meeting with. This email tells them the the date & time of the meeting etc, and also includes an iCal (.ics) file for them to put into their (Outlook usually, but also Apple or gmail) calendar.
The issue I am having is that (using the above examples) Outlook shows the meetings like this:
Meeting #1: Start: 23/06/2013 7:45pm, End: 23/06/2013 9:45pm
Meeting #2: Start: 23/12/2013 6:45pm, End: 23/12/2013 8:45pm
Note that it has adjusted the first one because of the BST/GMT thing.
The text of the .ics file contains this code:
Meeting #1:
BEGIN:VCALENDAR
...
DTEND:20130623T204500Z
DTSTART:20130623T184500Z
...
END:VCALENDAR
Meeting #2:
BEGIN:VCALENDAR
...
DTEND:20131223T204500Z
DTSTART:20131223T184500Z
...
END:VCALENDAR
So I am encoding the dates/times using the Z timezone (UTC). I understand this is why Outlook mis converting the UTC time into the BST time for #1 and leaving #2 alone (because GMT == UTC)
My question is: how do I stop this happening? I want the time the meeting is scheduled for to be the absolute, actual time, regardless of GMT/BST: 6:45pm
Should I be storing the date-times as UTC in the DB? How would this be done (I assume it would apply to all dates, not just meeting start dates). And how to re-convert them back into the actual datetime when I display them in the webapp?
Extra:
I have an entry in my initializers/time_formats.rb like this:
:ical => "%Y%m%dT%H%M00Z"
So dates come out like "20130623T184500Z". I use this when building the ics. And this I think is the issue - if the date/time is during BST I don't want to be using Z, but something else?
Your problem is your date/time format. You have:
DTSTART:20130623T184500Z
in your .ics file and this corresponds to 19:45 BST (as British summer time is UTC+1).
There are a few things you should do. First, you can simply remove the 'Z' from the end of your dates. This means that the times inherit the timezone of the calendar, or the underlying application.
This will work assuming that the machines which are running Outlook are all in the Europe/London timezone. If not, or if you want to be a bit safer, you should also specify the following after your BEGIN: VCALENDAR line:
X-WR-TIMEZONE:Europe/London
This specifies the default timezone for all dates which are not specified explicitly.
Finally, if this does not work for any reason then you need to define your datetimes explicitly. First you need to add a timezone definition for Europe/London to the calendar. The info you need is available at http://www.tzurl.org/zoneinfo-outlook/Europe/London.ics. Then you need to ensure that all datetimes are of the format:
DTSTART;TZID=Europe/London:20130623T184500
This last approach is the best, as it means that if your requirements expand to other timezones you will be able to handle them relatively easily.
Sorry to answer this myself, but in case anyone else runs into this here's what I found was the cause of my particular issue. Note that the answer above re timezones also makes sense!
My rails app is storing UTC datetimes in the DB (as is default)
But, it also thought it's own timezone was UTC, which also seems to be the default.
The upshot of that is essentially it was storing local dates, local to UTC anyway. Changing the app to know it was sitting in Europe/London made it so the dates in the DB are all now accurately UTC (meaning, they're an hour off if I'm currently in BST)
I can now use the Z datetime format in iCals, and outlook and the rails app both convert the UTC date back into the actual datetime for the viewing-user's locale (Europe/London for everyone at the moment). This is what I wanted.

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 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

How to deal with UTC times and Rails/ActiveRecord

I'm using Rails 3.2.8. When I generate a scaffold with a time field or datetime field. The HTML form field gets pre-populated with the current date/time in UTC. Great that it's pre-populated with the current date/time, but because it's in UTC, we have to change the time back 7 hours every time (which will sometimes require setting the day back 1 also, possibly month and year too). And then it seems the UTC time gets stored in the database, so I'll have issues displaying/editing it as well if I recorded it in our local time.
I looked at the Ruby documentation for the form helpers to deal with local time better, but I don't see anything relevant.
What's the best way to deal with editing and displaying dates and times in Rails?
UPDATE: To clarify, I like the idea that UTC time is stored in the database. I just want the time_select and datetime_select form helpers to edit the times in the user's local timezone. And, of course, I want to easily display the time in the user's local timezone.
You have to set the time zone to display on a per-user basis somewhere in a before/around_filter. This has to be done like:
Time.zone = "Kyiv"
An example can be found in its API: http://api.rubyonrails.org/classes/Time.html#method-c-zone-3D.
Run the rake time:zones:all to see all of them.
All the conversions has to be handled for you behind the scene.
This has helped me in the past. You can do things like:
Time.now.in_time_zone("Central Time (US & Canada)")
See Rails: convert UTC DateTime to another time zone

When is it appropriate to use Time#utc in Rails 2.1?

I am working on a Rails application that needs to handle dates and times in users' time zones. We have recently migrated it to Rails 2.1 and added time zone support, but there are numerous situations in which we use Time#utc and then compare against that time. Wouldn't that be the same as comparing against the original Time object?
When is it appropriate to use Time#utc in Rails 2.1? When is it inappropriate?
If you've set:
config.time_zone = 'UTC'
In your environment.rb (it's there by default), then times will automagically get converted into UTC when ActiveRecord stores them.
Then if you set Time.zone (in a before_filter on application.rb is the usual place) to the user's Time Zone, all the times will be automagically converted into the user's timezone from the utc storage.
Just be careful with Time.now.
Also see:
http://mad.ly/2008/04/09/rails-21-time-zone-support-an-overview/
http://errtheblog.com/posts/49-a-zoned-defense - you can use the JS here to detect zones
Hope that helps.
If your application has users in multiple time zones, you should always store your times in UTC (any timezone would work, but you should stick with the most common convention).
Let the user input in local time, but convert to UTC to store (and compare, and manipulate). Then, convert back from UTC to display in each users' local time zone.

Resources