为什么添加一个月只增加31天而不是一个月?

时间:2012-08-02 15:57:50

标签: ruby-on-rails timezone

确实非常奇怪的错误。我有一个项目每月克隆一次,将下一个对象设置为scheduled_on日期,+ 1.months将来。

但后来发生了这件事:

Sun, 01 Apr 2012 16:00:00 PDT -07:00
Tue, 01 May 2012 16:00:00 PDT -07:00
Fri, 01 Jun 2012 16:00:00 PDT -07:00
Sun, 01 Jul 2012 16:00:00 PDT -07:00
Wed, 01 Aug 2012 16:00:00 PDT -07:00
Fri, 31 Aug 2012 17:00:00 PDT -07:00 # <--- What in the..

代码:

def clone_object
  objects = []

  Time.zone = account.timezone
  Chronic.time_class = Time.zone

  now = last_scheduled_on.to_time # <- this would have been Wed, 01 Aug 2012 16:00:00 PDT -07:00

  new_date = now + 1.months

  new_schedule = Time.zone.parse new_date.strftime('%Y-%m-%d' + ' ' + original_scheduled_on.strftime('%H:%M:%S'))

  objects << clone!(:scheduled_on => new_schedule, :recurring_job_id => id)

end

这是实际代码的截断版本。但它包含了我正在合理地影响这个问题的所有部分。

所以问题是......怎么可能发生错误呢?

更新

我很确定这与时区有关。

以下是UTC中的日期:

In UTC :

Sun, 01 Apr 2012 23:00:00 UTC +00:00
Tue, 01 May 2012 23:00:00 UTC +00:00
Fri, 01 Jun 2012 23:00:00 UTC +00:00
Sun, 01 Jul 2012 23:00:00 UTC +00:00
Wed, 01 Aug 2012 23:00:00 UTC +00:00
Sat, 01 Sep 2012 00:00:00 UTC +00:00
Sun, 30 Sep 2012 23:00:00 UTC +00:00

这里他们被转换为太平洋地区:

In Pacific

Sun, 01 Apr 2012 16:00:00 PDT -07:00
Tue, 01 May 2012 16:00:00 PDT -07:00
Fri, 01 Jun 2012 16:00:00 PDT -07:00
Sun, 01 Jul 2012 16:00:00 PDT -07:00
Wed, 01 Aug 2012 16:00:00 PDT -07:00
Fri, 31 Aug 2012 17:00:00 PDT -07:00
Sun, 30 Sep 2012 16:00:00 PDT -07:00

我还注意到我在这里放置的代码对我的服务器不准确。服务器将Time.zone设置为上一个作业,设置为帐户的时区。这意味着(或者至少我认为这意味着),时区是浮动和动态的。但这让我感到困扰,因为加州的夏令时不会在11月而不是9月转换。

3 个答案:

答案 0 :(得分:0)

时间计算总是有问题的。 1.月可能是30天,31天,28天,或介于两者之间的任何东西。这取决于添加的月份,添加的内容,等等。因此,在进行日期算术时,不幸的是这样的错误和令人讨厌的常见问题。

要更正此代码,我会始终确保它选择您所在月份的月份的开头,如下所示:

new_date = (now + 1.months).beginning_of_month

这将永远是本月的第一天。

答案 1 :(得分:0)

答案 2 :(得分:0)

发生这种情况的原因是,当您添加两个DateTime时,DateTime对象将按天数添加并抹黑时区。

当您将两个Time对象一起添加时,它们将按小时添加,并包含任何时区差异。

小时差异的出现特别是因为两个DateTime被加在一起,超过了时区的变化。然后将时间作为相同的数字保存到UTC中的数据库,尽管事实上,当它们稍后在各自的时区中被解析时,它们将被偏移一个小时。