【问题标题】:Querying time of day from in a Rails datetime column (with time zones)从 Rails 日期时间列中查询一天中的时间(带时区)
【发布时间】:2014-01-11 01:41:38
【问题描述】:

我有一些新闻事件通过 postgresql 日期时间列存储在数据库中。但是,我想查询事件发生的时间。像

Event.where("occurred_at < '11:00'")

如果这是一个 postgresql 时间列,您可以这样做。

为了增加额外的皱纹,我将整个应用程序的 Rails 时区设置为东部时间

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

因此,如果您尝试以某种方式提取数据库时间,则该时间将采用 UTC,因此在半年期间,时间比较将关闭 1 小时(因为夏令时)。

我唯一能想到的就是维护一个单独的“时间”类型的数据库列。

编辑和澄清

这个问题的棘手部分是由于夏令时,UTC 偏移量在半年中会发生变化。 Time.zone.parse("2013-01-01 10:00") 给出 2013 年 1 月 1 日 10:00 EST -05:00(即 15:00 UTC),而六个月后 Time.zone.parse("2013- 07-01 10:00") 给出了 2013 年 7 月 1 日 10:00 EDT -04:00(即 14:00 UTC)。所以你不能只查询时间而不知道日期。

【问题讨论】:

  • 请参阅:stackoverflow.com/questions/4002958/…,了解 Rails 如何计算使用夏时制的地区的时区偏移量。
  • 你想查询时间,不管它发生在哪一天?
  • 是的,完全正确。不分昼夜

标签: ruby-on-rails ruby postgresql timezone activesupport


【解决方案1】:

我已经完全删除并重写了这个答案,因为旧答案只适用于 SQLite 而不是 OP 询问的 Postgres。

我能找到的唯一简单的方法(也是最像 Rails 的)是使用 gem。宝石是“tod”,可以在这里找到:https://github.com/JackC/tod

首先,您需要将gem 'tod' 添加到您的 Gemfile 并运行 bundle。然后,您将在数据库中创建 time 类型的列。

将此添加到您的 Event.rb 模型中

class Event < ActiveRecord::Base
  serialize :time, Tod::TimeOfDay

现在您可以获取时间戳并以序列化格式保存时间(即 13:45:00)。 TimeOfDay 类将为您提供调用和操作字段所需的方法。

在您的create 操作中,您可以有类似的内容(假设数据库中名为“tod_stamp”的列类型为time,以及一个名为:occured_at 的DateTime 对象):

def create
  @event = Event.new(event_params)
  tod_stamp = @event.occured_at.strftime('%H:%M:%S')
  @event.tod_stamp = TimeOfDay.parse(tod_stamp)

  respond_to do |format|
    if @event.save
      format.html { redirect_to @event, notice: 'Event was successfully created.' }
      format.json { render action: 'show', status: :created, location: @event }
    else
      format.html { render action: 'new' }
      format.json { render json: @event.errors, status: :unprocessable_entity }
    end
  end
end

现在,当您向数据库询问类似以下内容时:

my_events = Event.where("tod_stamp

您将根据事件记录的实际时间获得结果。如果您处于使用夏令时的时区,则无关紧要。您还可以在 tod 主页上以文档形式获取 TimeOfDay 解析、比较和操作的其他方法。

在我尝试重新发明轮子之前,我应该像往常一样寻找宝石。 :-) 希望这会有所帮助。

【讨论】:

  • 不错的答案。是的,时区部分是其中最棘手的部分。 Time.zone.parse("2013-01-01 10:00") 给出 2013 年 1 月 1 日 10:00 EST -05:00 而六个月后 Time.zone.parse("2013-07-01 10:00" ) 给出 2013 年 7 月 1 日 10:00 EST -04:00。因此,通过仅将时间(而不是日期)转换为 UTC,我将在半年内得到偏离 1 小时的错误。
  • 我认为当您询问 Rails 时返回的内容与 UTC 中数据库中的内容时,您仍然感到困惑。无论是一年中的什么时候,美国东部标准时间 08:00 将始终转换为世界标准时间 03:00。假设您在一月份,并且为刚刚发生的事件保存了 08:00:00 的时间戳,Rails 将在 DB 列中保存 03:00 的 UTC 日期时间。在 6 月,当您在 08:00 保存另一个时间戳时,Rails 仍会将其转换为 03:00。它正在更改 -04:00 和 -05:00,因为您(或您的计算机)已经在物理上改变了您的时钟向前或向后和小时。
  • 好的 cmets,但是 (1) UTC 时间确实随着夏令时而改变。 1 月 1 日美国东部标准时间 10:00 是世界标准时间 15:00,但 7 月 1 日美国东部标准时间 10:00 是世界标准时间 14:00。在 Rails 中,您可以使用 Time.zone.parse("2013-01-01 10:00").utc 进行验证。 (2) 你为什么在你的例子中查询'created_at'?我相信如果您查询 'occured_at',您会得到不同的答案。
  • 我的观点是 Rails 正在进行更正以便为您提供正确的结果,或者您不清楚自己要完成的工作。我在我的第一个原始答案中发布的代码允许您选择一个时间进行查询,比如说 09:00,并查询发生的_at 列,返回对时区和夏令时进行适当更正的结果,不管是什么创建时间戳或进行查询的时间。
  • 进行了更正并重新运行了这些查询,结果相同。你说“UTC时间确实会随着夏令时而改变。”那是错的。 UTC 时间是 UTC 时间。东部标准时间随夏令时而变化,任何遵守夏令时的时区也是如此。 Rails 意识到了这一点。这就是为什么 Rails 在存储在数据库中的时间戳和我正在编写的代码之间进行杂耍,所以我不必担心。
猜你喜欢
  • 2013-03-11
  • 1970-01-01
  • 1970-01-01
  • 2021-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多