JVK
JVK

Reputation: 3912

Rails4: Saving and displaying date in user's timezone

I am working on a rail4 app. Where I want to store dates in all mysql tables in UTC. However I store user's timezone in a specific table, called users. When user logs in, I get user's timezone form user table and save in session.

I am able to save date in all tables in UTC as default value of config.time_zone is UTC for activerecords and activemodels. But while displaying I want to show dates in user's timezone. As well as, when any user inputs a date/time in any html form, then I want to save it in the equivalent UTC format.

What is the best way to achieve this?

Upvotes: 0

Views: 2724

Answers (2)

p.matsinopoulos
p.matsinopoulos

Reputation: 7810

Rails, activerecord and MySQL will save all the timestamp fields in UTC. Without you having to do anything.

In your application.rb file where the configuration of the Application is done, you define the default time zone if you want the display of timestamps to take place on time zone different from UTC.

Hence

config.time_zone = 'Central Time (US & Canada)'

will display the timestamp fields (without you having to do anything special in other piece of code) using the Central Time.

When you want each of your users to have timestamps displayed in different time zone you can store the time zone in a column along side the user data. The column can be called time_zone and can contain the string of the user preferred time zone.

But, you have to tell the timestamp object to display itself to the specific timezone. This is done with the help of the method in_time_zone(timezone) that DateTime object responds to.

Example (when the default time zone is UTC):

1.9.3-p194 :004 > d = DateTime.new(2012, 9, 1, 6, 30, 0)
 => Sat, 01 Sep 2012 06:30:00 +0000 
1.9.3-p194 :005 > d.in_time_zone("Central Time (US & Canada)")
 => Sat, 01 Sep 2012 01:30:00 CDT -05:00 

Or you can change the time zone globally for the request at hand on a before or around filter. There is a documentation on internet if you do a google on that.

Read also this one: http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html for various alternatives to approach the problem.

Upvotes: 1

William Denniss
William Denniss

Reputation: 16336

You could store the time in UTC, and store the timezone separately. Timezones are commonly stored as a UTC-offset in seconds (seconds are the SI unit of time).

Then you can display it like so:

utime = Time.now.utc.to_i  # this value can be any format that Time.at can understand. In this example I'll use a unix timestamp in UTC. Chances are any time format you store in your DB will work.
 => 1375944780 

time = Time.at(utime)  # parses the time value (by default, the local timezone is set, e.g. UTC+08:00)
 => 2013-08-08 14:53:00 +0800 
time_in_brisbane = time.in_time_zone(ActiveSupport::TimeZone[36000])  # sets the timezone, in this case to UTC+10:00 (see http://stackoverflow.com/a/942865/72176)
 => Thu, 08 Aug 2013 16:53:00 EST +10:00 
time_brisbane.strftime("%d %b %Y, %l:%M %p %z")  # format with strftime however you like!
 => "08 Aug 2013,  4:53 PM +1000" 

Upvotes: 1

Related Questions