This page looks best with JavaScript enabled

【Rails】DateTimeで日付と時間を比較をする際の注意点

 ·   ·  ☕ 2 分で読めます
✏️

経緯を書いているとポエムになってしまったので、結論を先に書いておきます。

環境
Rails 5.2.4.2
Ruby 2.6.6

結論

  • DateTimeで日付を指定すると、インスタンスはDateクラスになる。
  • Dateクラスはタイムゾーンの情報を持っていないため、+0000になる。
  • DateクラスはDateTime/Timeクラスになった時点でタイムゾーンを持つ。
  • DateクラスとDateTime/Timeクラス(RailsだとActiveSupport::TimeWithZoneクラス)を比較するときはDateクラスをDateTimeクラスへ変換する必要がある。

ポエム

ことの起こりは深夜0時も回った頃。RSpecを走らせたところ、実装と関係のないところが落ちる。

調べてみたところ、どうやろ本当はreturnしないといけないところがreturnしていないようです。
してほしい挙動としては、「最後にツイートした日時が昨日より最近のときにreturnする」というもの。

1
    return if last_tweet.tweeted_at > DateTime.yesterday

というわけで、調べてみました。

デバッグ

1
2
[2] pry(#<RegisteredTag>)> DateTime.yesterday
=> Thu, 21 May 2020

うんうん、合ってる。

1
2
[3] pry(#<RegisteredTag>)> DateTime.yesterday.to_time
=> 2020-05-21 00:00:00 +0900

よかろう。

1
2
[4] pry(#<RegisteredTag>)> last_tweet.tweeted_at.to_time
=> 2020-05-21 00:30:02 +0900

そうだよね、では比較してみよう。

1
2
[6] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday.to_time
=> true

比較したらそうなるはずなんだ。では、元のコードに戻してみよう。

1
2
[5] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday
=> false

ファファファ????

ということで、改めて確認してみます。

日付の比較

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[33] pry(main)> DateTime.yesterday
=> Thu, 21 May 2020
[34] pry(main)> after_9
=> Thu, 21 May 2020 09:21:38 +0900
[35] pry(main)> DateTime.yesterday < after_9
=> true
[37] pry(main)> before_9
=> Thu, 21 May 2020 08:31:48 +0900
[38] pry(main)> DateTime.yesterday < before_9
=> false

8時台 < DateTime.yesterday < 9時台
ということで、DateTime.yesterdayにはタイムゾーンが適用されていないようです。

Date.yesterdayでもやってみた

1
2
3
4
5
6
[22] pry(main)> Date.yesterday
=> Thu, 21 May 2020
[23] pry(main)> Date.yesterday < before_9
=> false
[24] pry(main)> Date.yesterday < after_9
=> true

DateTimeと同じ挙動をします。

クラスを見てみる

1
2
3
4
[31] pry(main)> Date.yesterday.class
=> Date
[32] pry(main)> DateTime.yesterday.class
=> Date

あ、DateTimeで作ってもインスタンスのクラスはDateになるんですね。
つまり、Date.yesterdayDateTime.yesterdayは同じもののようです。

Share on

END
END
@aiandrox

目次