简体   繁体   中英

Rails Form Timezone ignored when saving record

I am having a problem with Rails Forms timezones.

I have a form that is used to setup Schedules. The form itself is quite simple and is given below.

<div class="row">
  <div class="col-sm-6">
    <h3>Create a new schedule</h3>
    <%= bootstrap_form_for(@schedule,label_errors: true, inline_errors: true) do |f| %>
            <%= f.text_field :title,required:true %>
            <%= f.text_field :description,required:true %>
            <%= f.datetime_local_field :start_time,required:true %>
            <%= f.datetime_local_field :end_time,required:true %>
            <%= f.text_field :cost_cents,required:true %>
            <%= f.button :submit %>
          <% end %>
  </div>
</div>

In my config/application.rb, I have set the timezone information as follows.

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

For the user, from the UI, they would be seeing a standard way to pick the date and time.

在此处输入图片说明

What is happening is that suppose i pick July 21 2018 04:00 PM as the start time and July 21 2018 05:00 PM as the end time, and since I am in IST time zone (+0530), what I expect is the database to store the data in UTC time by doing the appropriate conversions.

Instead what is happening is that the timezone is completely ignored and the time set on the form is being taken as UTC time and stored.

This is what I see in the Rails console

The incoming request is

    Parameters: {"utf8"=>"✓",
 "authenticity_token"=>"W1W6naJhEmXhqOJOBBhzQ3O+/HpHrP2WtVNtND+Yoh6mvgsTNr+2GG6T5FxDPRp+DyJAIA+esTpXw8HrDQ60Zw==", 
"schedule"=>{"title"=>"Test", "description"=>"Test 2",
 "start_time"=>"2018-07-21T16:00", 
"end_time"=>"2018-07-21T17:00",
 "cost_cents"=>"0"}, "button"=>""}

and the SQL generated is

        INSERT INTO "schedules" ("title", "description", "start_time", "end_time",
     "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7)
     RETURNING "id"  [["title", "Test"], ["description", "Test 2"], ["start_time", 
    "2018-07-21 16:00:00"], ["end_time", "2018-07-21 17:00:00"], ["user_id", 
    "8f4499ed-fa6f-43b8-be3e-228e3ed57fc6"], ["created_at", "2018-07-21 08:16:38.496829"], 
["updated_at", "2018-07-21 08:16:38.496829"]]

My question is,

How do I ensure that the the start_time and end_time values being passed from the browser get converted to UTC before saving?

Is there a way to change the value before being submitted from the browser so that Rails can take care of the appropriate conversion?

As per comments to your answer, try this:

class Schedule < ApplicationRecord

  before_save :convert_start_end_times_to_utc

  private
    def convert_start_end_times_to_utc
      start_time = Time.parse(start_time).getutc
      end_time = Time.parse(end_time).getutc
    end

end

before_save callback calls the correspondent method ( convert_start_end_times_to_utc ) which manipulates the attributes of the model. In this case start_time ( self.start_time ) and end_time ( self.end_time ). This is done before to save the record to database.

For callbacks see: http://guides.rubyonrails.org/active_record_callbacks.html

See this post for converting to utc: Convert DateTime String to UTC in rails

Ok so I think I have this working. Please let me know if this is on the right track.

What I did was add the momentjs-rails gem to my project. This gave me access to moment objects on the client side.

Then I modified the form so that the value is converted before the page submission happens and so the UTC time gets sent always.

<div class="row">
  <div class="col-sm-6">
    <h3>Create a new schedule</h3>
    <%= bootstrap_form_for(@schedule,label_errors: true, inline_errors: true) do |f| %>
            <%= f.text_field :title,required:true %>
            <%= f.text_field :description,required:true %>
            <%= f.datetime_local_field :start_time,required:true %>
            <%= f.datetime_local_field :end_time,required:true %>
            <%= f.text_field :cost_cents,required:true %>
            <%= f.button :submit %>
          <% end %>
  </div>
</div>
<script>
setTimeout(() => {
  $('.new_schedule').submit(function() {
  var start_time = moment($('#schedule_start_time').val());

  if (start_time.isValid()) {
    $('#schedule_start_time').val(start_time.clone().utc().format('YYYY-MM-DDTHH:mm'));
  }

  var end_time = moment($('#schedule_end_time').val());

  if (end_time.isValid()) {
    $('#schedule_end_time').val(end_time.clone().utc().format('YYYY-MM-DDTHH:mm'));
  }
});
},200);
</script>

This article was useful in coming up with this solution.. https://www.codementor.io/yitzofthebits/localize-time-traditional-rails-app-momentjs-twitter-clone-du107wwfg

So when i submit a form with

在此处输入图片说明

the database ends up storing the correct time in UTC.

 title  | description |     start_time      |      end_time       
--------+-------------+---------------------+---------------------
 Test 2 | Test 2      | 2018-07-21 12:30:00 | 2018-07-21 14:30:00

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM