datetime_select not displaying in utc

660 Views Asked by At

In my rails app, there are certain travel expenses where I want them set and displayed in raw UTC time.

I've added this to my config/application.rb

config.time_zone = 'UTC'
config.active_record.default_timezone = :utc

There seems to be a set (blank datetime_select), a save (stored in db as UTC), a display (<%= obj.date.to_utc %> and a modify (prefilled datetime_select).

I'm not having problems with the set, save or display. I've added a fix for the set - a before_save call to append ' +0000' to the time attribute. But I'm running into problems with modifying a datetime.

When I view the edit page, the datetime_select will display the previously save datetime. However, it displays the hours 6 hours earlier. Instead of 10pm, it will display 4pm.

Here is my form datetime_select:

<%= activity_log_item.object.date.utc %>
<%= activity_log_item.datetime_select :date,
  ampm: true,
  use_short_month: true,
  minute_step: 15, order: [:month, :day, :year, :hour, :minute] %>
</td>

The first line will print out the expected time (10pm), which I've confirmed is the same time in the db. However, the datetime_select form will display 4pm.

I would like all times in my app to be set, saved and displayed in UTC unless I explicitly parse it otherwise - using Time.zone = or .in_time_zone for example.

I'm using Rails 3.2.12.

1

There are 1 best solutions below

1
On

You will need to use Time.zone.now or Time.now.in_time_zone exclusively in your application in order to force Rails to use the timezone you've set in your config file. To default the date_select object to the time in UTC, simply initialize the new record with new_record.date = Time.zone.now, perhaps in your controller's new action.

Background

By default, Rails will convert all datetime fields to UTC before storing them in the database. When Rails reads a record back out, it will convert that UTC timestamp to be in the zone specified in your config file. Time.now will return a Time object in the timezone that your web server is configured to use. Time.zone.now will convert the system time to be in the timezone in your config file.

Example

Here's a practical example. My computer (aka the server) is configured in EST. I have a Rails app configured to use PST. The database stores everything in UTC

1.9.3p286 > Time.now
 => 2013-11-12 13:33:40 -0500 
1.9.3p286 > Time.now.zone
 => "EST" 
1.9.3p286 > Time.now.in_time_zone
 => Tue, 12 Nov 2013 10:33:40 PST -08:00 
1.9.3p286 > Time.now.in_time_zone.zone
 => "PST" 
1.9.3p286 > Time.zone.now
 => Tue, 12 Nov 2013 10:33:40 PST -08:00 
1.9.3p286 > Time.zone.now.zone
 => "PST" 
1.9.3p286 > Time.parse("12:34:56")
 => 2013-11-12 12:34:56 -0500 
1.9.3p286 > Time.parse("12:34:56").zone
 => "EST" 
1.9.3p286 > Time.parse("12:34:56").in_time_zone
 => Tue, 12 Nov 2013 09:34:56 PST -08:00 
1.9.3p286 > Time.parse("12:34:56").in_time_zone.zone
 => "PST" 
1.9.3p286 > Time.zone.parse("12:34:56")
 => Tue, 12 Nov 2013 12:34:56 PST -08:00 
1.9.3p286 > Time.zone.parse("12:34:56").zone
 => "PST" 
1.9.3p286 > Time.parse("12:34:56 UTC")
 => 2013-11-12 12:34:56 UTC 
1.9.3p286 > Time.parse("12:34:56 UTC").zone
 => "UTC" 
1.9.3p286 > Time.parse("12:34:56 UTC").in_time_zone
 => Tue, 12 Nov 2013 04:34:56 PST -08:00 
1.9.3p286 > Time.parse("12:34:56 UTC").in_time_zone.zone
 => "PST" 
1.9.3p286 > Time.zone.parse("12:34:56 UTC") # Note that the time is parsed as UST and then converted to PST
 => Tue, 12 Nov 2013 04:34:56 PST -08:00 
1.9.3p286 > t = TripTicket.first
 => #<TripTicket ...> 
1.9.3p286 > t.appointment_time # Reading a datetime field from the database
 => Thu, 21 May 2009 10:29:11 PDT -07:00 
1.9.3p286 > t.appointment_time.in_time_zone # No additional conversion because rails already converted it to PST
 => Thu, 21 May 2009 10:29:11 PDT -07:00 
1.9.3p286 > t.appointment_time_before_type_cast # The raw value in the database, stored as UTC
 => "2009-05-21 17:29:11.614345" 
1.9.3p286 > t.appointment_time.utc
 => 2009-05-21 17:29:11 UTC 
1.9.3p286 > t.appointment_time = Time.parse("12:34:56") # Set the time in the server local timezone, i.e. EST
 => 2013-11-12 12:34:56 -0500 
1.9.3p286 > t.appointment_time # Reading the time. Note it's converted to PST already
 => Tue, 12 Nov 2013 09:34:56 PST -08:00 
1.9.3p286 > t.appointment_time.zone
 => "PST" 
1.9.3p286 > t.appointment_time.utc
 => 2013-11-12 17:34:56 UTC 
1.9.3p286 > t.save
 => true 
1.9.3p286 > t.reload
 => #<TripTicket ...> 
1.9.3p286 > t.appointment_time_before_type_cast
 => "2013-11-12 17:34:56"