LoginSignup
0
1

More than 3 years have passed since last update.

Timeオブジェクトの次の値ってどうやってだすのが正しい?

Posted at

概要

RubyでTimeオブジェクトを扱ってると境界問題にぶち当たる。
普通にやってるとRangeオブジェクトなりを使ってそこまで問題になることはないが、普通じゃないやり方をしていて躓いた。

環境

Ruby: 2.6.2
Rails: 5.1.2

そもそもハマった問題

Time.now.end_of_day
=> 2020-05-15 23:59:59 +0900

これの次の値を取りたい。
※ end_of_dayはわかりやすいから使ってるだけであって、とある日の先端、終端が欲しいわけではない。飽くまでもTimeオブジェクトの次の値

真っ先に思いついたのがこれ

Time.now.end_of_day + 1
=> 2020-05-16 00:00:00 +0900

ただ、これは間違い

(Time.now.end_of_day + 1).iso8601(3)
=> "2020-05-16T00:00:00.999+09:00"

+ 1 は飽くまでも1秒加算であって、次の値に移行するわけではないので使えない。

Timeクラスを漁ってるとすごくそれっぽいメソッドを見つけた
https://docs.ruby-lang.org/ja/latest/method/Time/i/succ.html

Time.now.end_of_day.succ
(pry):109: warning: Time#succ is obsolete; use time + 1
=> 2020-05-16 00:00:00 +0900

しかし、時代遅れだから使うなって怒られる上 + 1 と同じことしてるだけらしい。だめ。

解決方法

結局良さげな方法はわからなかったからゴリ押し

例の問題だと少数9桁までしか見ていない

(Time.now.end_of_day + 1).iso8601(10)
=> "2020-05-16T00:00:00.9999999990+09:00"

つまり、1ナノ秒加算してやればいいわけだ

(Time.now.end_of_day + 1/1000000000.0).iso8601(9)
=> "2020-05-16T00:00:00.000000000+09:00"

できた!

おまけ

Time.now.iso8601(10)
=> "2020-05-15T19:56:55.4979370000+09:00"

Time.new(2020, 5, 16, 16, 59, 59.3).iso8601(9)
=> "2020-05-16T16:59:59.299999999+09:00"

Time.parse("2020-05-16T16:59:59.3+09:00").iso8601(9)
=> "2020-05-16T16:59:59.300000000+09:00"

Time.parse("2020-05-16T16:59:59.35555555555555555555+09:00").iso8601(20)
=> "2020-05-16T16:59:59.35555555555555555555+09:00"

だめだった

結論

  • Timeオブジェクト内の小数秒の桁数は無限に持てる
  • そのため次の値という概念がそもそも無い
  • ただ、Timeオブジェクトの生成の仕方に依存するので、DBから引っ張ってくるならその有効桁数を考慮してやれば無理やり作れなくはない
  • そもそも素直にRangeオブジェクトを使うなりして比較すべき
0
1
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
0
1