Timezone Alarm in Rails

I have an application that uses the following configuration:

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.
  config.time_zone = 'Amsterdam'
  config.active_record.default_timezone = :utc
end

      

I offer the user a form that asks for a separate date (datepicker) and a time field (drop down). In the before method, I concatenate the date and time into one datetime field. It seems that the Time object somehow occurs in UTC, while the Date object doesn't care about it.

When storing this in the database, somehow the timezone is adjusted, but the time is 2 hours. Is it because the time is still in UTC? How can I compensate for this in the same way as the summer savings?

Sample code

The form:

= simple_form_for @report do |f|
  .row
    .col.s6
      = f.association :report_category
    .col.s6
      = f.association :account
  .row
    .col.s6.l4
      = f.input :day, as: :string, input_html: {class: 'datepicker current-date'}
    .col.s6.l4
      = f.input :time, as: :string, input_html: {class: 'timepicker current-date'}

      

Callback in model

  def create_timestamp
    self.date = day.to_datetime + time.seconds_since_midnight.seconds
  end

      

After saving, there is a 2 hours difference between the time I selected in the form.

Here's how it looks after creating a new post, where the time is at the same time as the post, to see the difference:

 date: Sat, 20 May 2017 16:10:00 CEST +02:00,
 created_at: Sat, 20 May 2017 14:10:33 CEST +02:00,
 updated_at: Sat, 20 May 2017 14:10:33 CEST +02:00,
 deleted_at: nil,
 time: 2000-01-01 14:10:00 UTC,
 day: Sat, 20 May 2017,

      

As you can see, the time is stored in UTC, but in fact it is 14:10 CET +0200, where I created the entry. Thus, it can cause inconsistency. Which you can see in the date where the time is +2 hours from created_at.

thank

+3


source to share


3 answers


Keep everything in UTC and undo the differences.

When the form submits the fields and you concatenate the DateTime object, it will be interpreted as UTC ... so you have to do the transformations after the information is submitted.



Model.rb

    # Set the Proper DateTie
    def create_timestamp
        # Create the DateTime
        date = day.to_datetime + time.seconds_since_midnight.seconds
        # Set the timezone in which you want to interpret the time
        zone = 'Amsterdam'
        # Find the offset from UTC taking into account daylight savings
        offset = DateTime.now.in_time_zone(zone).utc_offset / 3600
        # Back out the difference to find the adjusted UTC time and save it as UTC with the correct time
        self.date = date - offset.hours
    end

      

+3


source


PART 1

Timestamps are stored in UTC by default and this is probably the best way to do it. If you move from one server environment to another, you don't want all of your times to switch just because you were switching time zones.

If you want to know what timestamp is in your local timezone, you just need to ask about it like this:

obj.updated_at.localtime

      

PART 2:

Ruby doesn't respect Rails timezone configuration. This means that if your system uses America / Sao_Paulo time zone, and your application uses America / Los_Angeles, Ruby will still consider the old configuration.

Time.now.zone
#=> "BRST"

Time.zone = "America/Los_Angeles"
#=> "America/Los_Angeles"

Time.now.zone
#=> "BRST"

      

It is better to leave the application's timezone in UTC and instead allow each individual user to set their own timezone.

Add timezone attribute to user

create_table :users do |t|
  t.string :time_zone, default: "UTC"
  ...
end

      

Get time zone from form

<%= f.input :time_zone %>

      

We can then use around_action to set the user's preferred timezone:

# app/controllers/application_controller.rb
around_action :set_time_zone, if: :current_user

private

def set_time_zone(&block)
  Time.use_zone(current_user.time_zone, &block)
end

      

We pass the current user's timezone to the use_zone method in the Time class (a method that was added by ActiveSupport). This method expects a block to be passed to it and sets the timezone for the duration of that block so that when the request is complete, the original timezone will be set back.



DOs:

Get the current time: Time.zone.now

Get the day: Time.zone.today

Time from timestamp: Time.zone.at(timestamp)

Analysis time: Time.zone.parse(str)

DONT'S:

Time.now

DateTime.now

Date.today

Time.at(timestamp)

Time.parse(str)

Use Time.current

instead Time.now

.

Use Date.current

instead Date.today

.

Check out this post for more information on Rails timezone:

working-with-time-zones-in-ruby-on-rails

+1


source


As far as I know, the method to_datetime

uses the system time, so the final date is generated with your system timezone.

self.date = day.to_datetime + time.seconds_since_midnight.seconds

      

creates Sat, 20 May 2017 14:10:00 +0000

. Since it is already in UTC, it is stored as-is in the database, and later Rails adds 2 hours to display it in the Amsterdam timezone.

You can fix this by avoiding to_datetime

conversion

self.date = day + time.seconds_since_midnight.seconds

      

Or, if day

is a string, you can do

self.date = Time.zone.parse(day) + time.seconds_since_midnight.seconds

      

Also I recommend this article about the problems with different time zones.

0


source







All Articles