Date.today > Date.yesterday は必ずしも真ではなかった

Ruby on Railsの話です。

まとめ

もうすこし詳しく

Date.today に関してはまとめに書いた以上の説明は特になし。

Date.yesterday に関して少しだけ。

    # Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
    def yesterday
      ::Date.current.yesterday
    end

https://github.com/rails/rails/blob/5f3ff60084ab5d5921ca3499814e4697f8350ee7/activesupport/lib/active_support/core_ext/date/calculations.rb#L37-L40

Date.current に対してyesterdayを呼んでる。Date.currentはこう。

    # Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
    def current
      ::Time.zone ? ::Time.zone.today : ::Date.today
    end

https://github.com/rails/rails/blob/5f3ff60084ab5d5921ca3499814e4697f8350ee7/activesupport/lib/active_support/core_ext/date/calculations.rb#L47-L50

アプリケーションでタイムゾーンが設定されている場合は Time.zone.today を返す。

気づいたきっかけ

お仕事。

%m/%d のようなフォーマットで渡されるStringをDateに変換し、その日付が今日より前だった場合に特定の処理を実施する、というロジックがあった。

date_string = '01/29' # 外部から渡される(CSVファイルとか...

if date_string.to_date < Date.today
  # do something
end

対象のシステムはUTCで動いているので、この処理をJSTの1月30日00:00~08:59に実行すると Date.today は「1月29日」になる。 そのため date_string.to_date < Date.todayfalse になる。

これは想定した挙動ではなかった。

気づいたのはたまたま朝9時前にテストコードを実行した日があり、CIがfailしていたからだった。

テストコードでは date_stringDate.yesterday.strftime('%m/%d') で生成していた。
アプリケーションのタイムゾーンの設定はJSTになっているため、このテストをJSTの1月30日00:00~08:59に実行すると、タイムゾーンが考慮されてDate.yesterdayは1月29日になる。

気づけてラッキーだった〜。

で、こういう修正をした。

- if date_string.to_date < Date.today
+ if date_string.to_date < Time.zone.today
  # do something
end

アプリケーションのタイムゾーンを考慮して「今日」を判定できるようになったのでおっけ。

ひとこと

Date.today > Date.yesterday が真とは言い切れないの、時空が歪んでいる感じがしてすごい。

おしまい。