【问题标题】:how do I add N days to time T (accounting for Daylight Savings Time)?如何将 N 天添加到时间 T(考虑夏令时)?
【发布时间】:2013-01-15 07:46:26
【问题描述】:

我有一个 Time 对象 T。将 N 天添加到 T 的合理方法是什么?

我想出的最好的感觉有点受折磨:

require 'date'
def add_days(time, days)
  time.to_date.next_day(days).to_time
end

P.S.:如果你在美国,正确答案必须满足:

add_days(Time.new(2013, 3, 10, 0), 1) == Time.new(2013, 3, 11, 0)

如果您在欧盟,正确答案必须满足:

add_days(Time.new(2013, 3, 31, 0), 1) == Time.new(2013, 4, 1, 0)

P.P.S:这是一个 Ruby 问题,而不是 Rails 问题。

【问题讨论】:

    标签: ruby time


    【解决方案1】:

    Time 有一个 + method 接受秒。

    N = 3
    
    t = Time.now + N * 86400 # 24 * 60 * 60 
    

    或者,如果您引入 ActiveSupport,会更容易

    require 'active_support/core_ext'
    
    t = Time.now + N.days
    

    你显然可以自己做帮手

    class Fixnum
      def days
        self * 86400
      end
    end
    
    t = Time.now # => 2013-01-31 16:06:31 +0700
    
    t + 3.days # => 2013-02-03 16:06:31 +0700
    

    【讨论】:

    • 像这样添加秒数会在 DST 边界上给出错误答案
    • @FrederickCheung:啊,没想到。但它更简单。此外,它满足 OP 的规范。请随意添加您的答案,我会删除我的。
    • @SergioTulentsev:您的答案不符合原始规范 - 请尝试“正确答案必须满足”以了解我的意思。
    • @FrederickCheung:另一方面,你确定 DST 吗?你能提供一个有问题的sn-p吗?
    • 这很令人困惑。你在哪个时区? (更重要的是:你的夏令时/夏令时转换是什么时候?)
    【解决方案2】:

    ActiveSupport::TimeWithZone 似乎处理得很好

    > t1 = ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse('2013-03-10')
     => Sun, 10 Mar 2013 00:00:00 EST -05:00 
    

    注意下面的类类型:

     > t1.class
     => ActiveSupport::TimeWithZone 
    

    注意从上面的 EST 到下面的 EDT 的变化:

    > t1 + 1.day
     => Mon, 11 Mar 2013 00:00:00 EDT -04:00 
    

    【讨论】:

    • 实际上可以这样做:t1 = ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse('2013-03-12').dst? 关键是Eastern Time。如果您像我一样使用ESTEDT,它将始终在这些时区显示ET 当前是否在DST 中。
    • 很好的答案,虽然我最初询问的是没有 ActiveSupport 的 Ruby 环境。现在事情已经更好地模块化了,包含独立的 ActiveSupport 来获得这个功能是合理的。
    • 注意所有不想调整的人,您可以在添加时间之前将其转换为 utc,然后再将其转换回来,因为 utc 没有夏令时(datetime.utc + 1.weeks).to_datetime # will not adjust
    【解决方案3】:

    似乎已经成为我的风格,我正在回答我自己的问题。

    由于跨 DST / ST 的转换相当罕见(在世界许多地方,不存在),更有效的方法是首先添加 (n_days * 24 * 60 * 60) 秒,然后检查 UTC 偏移量已经改变。如果有,则创建一个更正的时间对象。

    像这样:

    def add_days(time, n_days)
      t2 = time + (n_days * 24 * 60 * 60)
      utc_delta = time.utc_offset - t2.utc_offset
      (utc_delta == 0) ? t2 : t2 + utc_delta
    end
    

    这种方法避免了许多额外的对象创建,并正确处理夏令时之间的转换(至少在我当前的时区,太平洋时间):

    >> t1 = Time.new(2013, 3, 10, 0, 0, 0)
    => 2013-03-10 00:00:00 -0800  # midnight Mar 3, 2013 Pacific Standard Time
    >> t2 = add_days(t1, 1)
    => 2013-03-11 00:00:00 -0700  # midnight Mar 4, 2013 Pacific Daylight Time
    >> t2 - t1
    => 82800.0                    # a shorter than usual day
    
    >> u1 = Time.new(2013, 11, 3, 0, 0, 0)
    => 2013-11-03 00:00:00 -0700  # midnight Nov 3, 2013 Pacific Daylight Time
    >> u2 = add_days(u1, 1)
    => 2013-11-04 00:00:00 -0800  # midnight Nov 4, 2013 Pacific Standard Time
    >> u2 - u1
    => 90000.0                    # a longer than usual day
    

    【讨论】:

      【解决方案4】:

      这有点横向回答,但是因为在您最初的问题中,您并不关心 Time 的 HMS 部分,所以使用 Date 对象不是更好吗?

      require 'date'
      t=Time.now
      d=Date.parse(t.to_s)
      puts d+1 # => gives you tomorrow's day (YMD)
      

      编辑:添加require 'date' 以提高答案的全面性,如 cmets 部分所述。

      【讨论】:

      • 这是正确答案。不过,在调用 Date.parse 方法之前必须require 'date'
      猜你喜欢
      • 2017-12-26
      • 2023-03-26
      • 1970-01-01
      • 2011-08-14
      • 1970-01-01
      • 2013-09-22
      • 1970-01-01
      • 2021-01-21
      • 2015-08-04
      相关资源
      最近更新 更多