Rails - 奇怪的 time_select 错误行为

Rails - Strange time_select buggy behavior

我的 rails 回购示例在这里: https://github.com/johndel/strange_timeselect

这里可以显示奇怪行为的复制: https://captain24.herokuapp.com/availabilities/new

我在使用 time_select 时遇到以下问题: 在带有 postgresql 的新 rails 应用程序中,我有一个模型 (availabilities),其中有一个 started_at 列,在 postgres 中是 time 类型。当我创建一条新记录时,它会保存 started_at 值,比所选记录早一小时。在更新时它工作正常。

我注意到以下几点: 我只能在 heroku 上复制它(在本地我无法复制它)。它仅适用于创建,并且仅在我在 application.rb 上设置 default_timezone 后才有效,如您在此 commit.

上所见

该应用程序还有另一个名为 tasks 的模型,其中包含一个名为 started_at 的列,类型为 datetime。这个总是正确的,但它节省了与 availabilities.

time 字段不同时区的时间

如果我在 time_select 字段上添加 ignore_date: true 可以修复它,但我想知道为什么会这样?这正常吗,我错过了什么?或者是一些非常奇怪的 ruby 或 rails 错误?还是 heroku 上 postgresql 的问题/配置错误?

更新: 因为 @max 让我进一步解释代码,所以这里是:

关于代码,现在它只是两个脚手架资源,带有命令rails g scaffold tasks started_at:datetimerails g scaffold availabilities started_at:time。所以一个是 tasks,另一个是 availabilities,每个模型只有一列。我还在 config/application.rb 中添加了这些代码行:

config.time_zone = 'Athens'
config.active_record.default_timezone = :local

可用性表单的代码是这样的(普通脚手架):

<%= form_with(model: task, local: true) do |form| %>
  <% if task.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(task.errors.count, "error") %> prohibited this task from being saved:</h2>

      <ul>
        <% task.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :started_at %>
    <%= form.time_select :started_at %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

可用性迁移代码是这样的:

class CreateAvailabilities < ActiveRecord::Migration[6.0]
  def change
    create_table :availabilities do |t|
      t.time :started_at

      t.timestamps
    end
  end
end

如果添加上面的代码并部署到heroku上,就能重现问题。

所以关于上面代码的问题是,当我创建一个 availability 记录时,它比选择的时间少一小时。这仅在创建时发生。 task 记录不是这种情况,因此它可能与 time postgres 类型和 active_record 时区有关。这只发生在 heroku 上,因为您可以查看上面的 link。

发生这种情况是因为您需要考虑服务器的时区。 您需要将 Heroku 配置为 well.The 命令来更改 Heroku 时间设置如下所示

heroku config:add TZ="Europe/Paris"

并且以 UTC 以外的任何其他格式保存日期都不是一个好主意

Details

编辑

要回答为什么更新方法工作方式不同的问题,您必须检查 rails.Rails 呈现的表单创建额外的三个隐藏字段

availability[started_at(1i)],

availability[started_at(2i)]

availability[started_at(3i)]

在创建表单中,这些字段的默认值为 2020,5,19

但在编辑表单中,它们的值为 2000,1,1

我有根据的猜测是他们搞乱了夏令时,因此造成了异常。

有两个不同的问题。

1) 本地与 Heroku 的不同场景。 - 您的 schema.rb 文件将 "started_at" 列为日期时间,而在迁移中,它是 "time" - Heroku 运行迁移,但 bin/rails db:setup 从 schema.rb 加载,因此您在本地看不到问题,因为它被保存为日期时间。

2) 时间列的问题是缺少日期,Ruby 必须猜测。当您创建新记录时,表格会预先填写当前日期,在 post 时是五月(一个夏季月份),并将在 EEST 中进行解析。 传入属性 "2020", "05", "20", "11", "00" 将被解析为 Wed, 20 May 2020 11:00:00 EEST +03:00

然后将其作为 -3 小时的时间偏移量保存在数据库中 8:00:00

下次从数据库加载时,没有附加日期,因此 Rails 使用 "2000", "01", "01" 的日期假设对其进行解析,结果为 Sat, 01 Jan 2000 10:00:00 EET +02:00。请注意,在这种情况下只添加了两个小时,因为它是 EET。

当您编辑时,表格会预先填写当前的可用性 started_at Sat, 01 Jan 2000 10:00:00 EET +02:00。当您更新时,由于表格中的日期现在是一月,因此使用 EET 时区对其进行解析并立即保存。由于用于显示和编辑的日期相同,因此现在看起来 'correct'

如何解决?这取决于应用程序,我不是专家,但有一些想法:-

1) 有人会说没有日期时间本来就没有意义。也许您可以考虑改用 datetime 列。希腊中午 12 点的可用性对于不同时区的人来说可能意味着不同。

2) 您可以使用 tod gem 之类的东西来处理时间。

3) 您可以将其存储为从上午 12 点开始的整数偏移量。