【问题标题】:rails - convert DateTime to UTC before saving to serverrails - 在保存到服务器之前将 DateTime 转换为 UTC
【发布时间】:2013-12-21 11:21:08
【问题描述】:

我不知道怎么做,但我的控制台和服务器对于 DateTime.now 有两个不同的时区。如果我在控制台中运行 DateTime.now,则会返回以下内容:

Wed, 04 Dec 2013 14:27:23 -0500 

但是,我的任务模型中有以下内容:

def self.already_expired
    where("time_frame < ?", DateTime.now)
end

上面代码的目标是在“time_Frame”是过去日期的任务上触发一些任务方法。这是我调用该方法的地方:

task :change_it => :environment do
  puts "yo yo you yo you"
  @tasks = Task.already_expired
  @tasks.each do |task|
        puts "Kalabar" + task.inspect + "now time is:" + DateTime.now.to_s
  end
end

服务器日志显示:

Task Load (1.2ms)  SELECT "tasks".* FROM "tasks" WHERE (time_frame < '2013-12-04 17:25:37')
    Kalabar#<Task id: 3, title: "task 1 - past", content: "Should show this one", 
        created_at: "2013-12-04 17:05:53", updated_at: "2013-12-04 17:05:53", 
        schedule_id: 2, amount: nil, time_frame: "2013-12-02 08:15:00">
        now time is:2013-12-04T12:25:37-05:00
    Kalabar#<Task id: 5, title: "another task - past", 
        content: "should show this one", created_at: "2013-12-04 17:11:23", 
        updated_at: "2013-12-04 17:11:23", schedule_id: 3, amount: nil, 
        time_frame: "2013-12-03 02:07:00">now time is:2013-12-04T12:25:37-05:00
    Kalabar#<Task id: 6, title: "another task - future", 
        content: "should not show at first", created_at: "2013-12-04 17:11:23",
        updated_at: "2013-12-04 17:25:09", schedule_id: 3, amount: nil, 
        time_frame: "2013-12-04 12:27:00">now time is:2013-12-04T12:25:37-05:00

因此,它将 time_frame 与 UTC 时间的 '2013-12-04 12:25:37 进行比较。但是,当我让它打印 DateTime.now.to_s 时,它会打印“2012-12-04 08:15:00”。

我想在所有内容进入数据库之前将其转换为 UTC。我也不明白为什么它有两个不同的时区 DateTime.now 这是我的问题

  1. 为什么 DateTime.now 有两个不同的时区
  2. 如何在保存到数据库之前将时区转换为 UTC?

更新:

我将以下内容放入我的 application.rb 文件中:

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

现在用户输入东部时间的日期时间,并将其存储为 UTC 时间。所以现在 time_frame 正在与 DateTime.now 进行正确比较。问题是,如果我放置 time_frame,它会将其放置在东部时间。我不太确定发生了什么事。它将它存储在UTC,但在东部时间打印。这是危险的/正确的吗?我在任务代码块中添加了一个“puts time_Frame”,如下所示:

task :change_it => :environment do
  @tasks = Task.already_expired
  @tasks.each do |task|
        puts "Kalabar" + task.inspect + "now time is:" + DateTime.now.to_s
      puts "time_frame is:" + task.time_frame.to_s
  end
end

它会输出这个:

Kalabar#<Task id: 3, title: "task 1 - past", content: "Should show this one", created_at: "2013-12-04 17:05:53", updated_at: "2013-12-04 17:05:53", schedule_id: 2, amount: nil, time_frame: "2013-12-02 08:15:00">now time is:2013-12-04T15:27:22-05:00
time_frame is:2013-12-02 03:15:00 -0500
Kalabar#<Task id: 7, title: "Task 1 - past by days", content: "Should always show", created_at: "2013-12-04 20:20:40", updated_at: "2013-12-04 20:20:40", schedule_id: 4, amount: nil, time_frame: "2013-12-02 09:02:00">now time is:2013-12-04T15:27:22-05:00
time_frame is:2013-12-02 04:02:00 -0500
Kalabar#<Task id: 8, title: "Task 2 - past by minutes", content: "should always show", created_at: "2013-12-04 20:20:40", updated_at: "2013-12-04 20:20:40", schedule_id: 4, amount: nil, time_frame: "2013-12-04 20:15:00">now time is:2013-12-04T15:27:22-05:00
time_frame is:2013-12-04 15:15:00 -0500

所以,如您所见,在数据库中它是 UTC,但当它输入时它是东部时间。为什么要这样做/危险吗?

【问题讨论】:

    标签: ruby-on-rails ruby datetime time datetime-format


    【解决方案1】:

    你所有的时间都会以UTC格式保存到数据库中,这是activerecord的默认行为。你不需要转换它。每当您初始化当前时间时,它都会返回一个时间对象,这意味着无论时区如何,它都会指向特定时间。无论您何时进入不同的时区,如果您将其转换为 UTC,所有时间都指向同一时间。所以,我认为查找当前时间应该没有任何问题,因为时间将如何存储在各自的 UTC 时间中。

    时区问题仅在您尝试使用字符串(或通过传递 DateTime 属性)初始化时间对象时发生。使用系统时区解析时间对象,而不是使用 application.rb 中设置的时区。所以在这种情况下,最好使用 ActiveSupport 时区对象来解析时间。

    【讨论】:

      【解决方案2】:

      您可以在config/application.rb中设置自定义时区

      # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
      # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
      # config.time_zone = 'Central Time (US & Canada)'
      

      【讨论】:

      • 我试过了。它似乎不起作用。我的代码有问题吗:Time.zone = 'UTC'
      • 你需要这样设置:config.time_zone = 'Central Time (US &amp; Canada)',使用rake time:zones:all查看时区名称列表。
      • 谢谢,我试过了,它奏效了(有点/我认为)。我更新了问题。
      • @Philip7899 你应该使用DateTime.current
      • 为什么?我试过了,它输出的内容与 DateTime.now 完全相同。
      【解决方案3】:

      1) DateTime.now 返回一个 DateTime obj,您可以将它转换为您想要的任何时区(它仍然是它的样子),如果您尝试对 ActiveRecord 进行任何操作,ActiveRecord 将首先将 DateTime obj 转换为 UTC默认情况下,然后执行工作。

      2) 在将任何日期时间保存到数据库之前,请确保它是 DateTime obj,如果您获取的日期时间是字符串,并且您知道时区,则可以使用 DateTime.parse 将其转换为 DateTime obj:

      irb(main):014:0> s = "2013-12-02 09:00:00 EST"
      => "2013-12-02 09:00:00 EST"
      irb(main):015:0> sdt = DateTime.parse(s)
      => Mon, 02 Dec 2013 09:00:00 -0500
      

      【讨论】:

        【解决方案4】:

        我不认为您正在查看 2 个时区。数据库中的所有时间(由task.inspect 显示)都是UTC。你是对的,当运行DateTime.now 时,你会得到一个代表当前时区时间的对象。很酷的事情是 ActiveRecord 足够聪明,可以将该对象更改为 UTC 以用于实际的 sql 命令。在控制台中试试这个:

        Task.where("time_frame < ?", DateTime.now).to_sql
        

        它应该将 DateTime.now 转换为 UTC 表示,因此您应该得到您期望的结果。这行得通吗?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-01-04
          • 1970-01-01
          • 1970-01-01
          • 2012-12-05
          • 2011-09-29
          • 2011-02-11
          • 2012-02-02
          • 1970-01-01
          相关资源
          最近更新 更多