LoginSignup
6
2

More than 5 years have passed since last update.

RubyのTime系の型は小数点以下も保持するので気をつける

Last updated at Posted at 2018-12-07

Rubyなら Time.now 、Railsなら Time.zone.now などで使う TimeTimeWithZone 意識して使わないと思わぬバグを生む原因なのでその事例を箸休め的にご紹介。

環境

$ ruby -v
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-darwin18]
$ bundle exec rails -v
Rails 5.2.1

ISO8601の形式に沿う形式出力がRubyにあり、オプションとして引数に値を入れると小数点以下の値も付随してくれるので、そこで確認することができる。

require 'time'
=> true

now_time = Time.now
#=> 2018-12-01 11:42:01 +0900

now_time.iso8601
#=> "2018-12-01T11:42:01+09:00"

# .iso8601({{小数点以下の桁数}})で確認できる
now_time.iso8601(5)
#=> "2018-12-01T11:42:01.69147+09:00"

11:42:01.69147 となり小数点以下の秒数を保持していることがわかる

起こりうるケース

日時だけ保存するデータ(主にMySQLなどのRDSのDateTime等)は小数点以下の秒数をもっていないことが多い。そのため範囲比較をするときに目に見えにくい範囲も比較してしまい。予期せぬ挙動をすることがある。

期間の終了を term_end として人が設定した値として持ち、それに対して特定の月の最終日と合致するかを確認する。

## 期間の終了日(今回はStringから起こす形)
term_end = "2018/12/31 23:59:59".in_time_zone
# => Mon, 31 Dec 2018 23:59:59 JST +09:00

## 特定の月
month_start_at =  "2018/12/01 00:00:00".in_time_zone
# => Sat, 01 Dec 2018 00:00:00 JST +09:00

## 特定の月の最終日
month_start_at.end_of_month
# => Mon, 31 Dec 2018 23:59:59 JST +09:00

irb上で出力される見た目は同じだからイコールになりそうだが…

month_start_at.end_of_month == term_end
=> false

小数点のレベルで相違があるのでそうはならないのでそうはならない。

month_start_at.end_of_month.iso8601(5)
# => "2018-12-31T23:59:59.99999+09:00"

term_end.iso8601(5)
# => "2018-12-31T23:59:59.00000+09:00"

関連リンク

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2