ちょっとテンションが上がってしまったのでTodについてまとめました。
多機能なTod::TimeOfDay
tod
をインストールするとTod::TimeOfDay
クラスが使えるようになります。
- 時刻のパース
- 比較
- フォーマット出力
- 演算
- その他ちょろちょろ
- Railsのシリアライズもサポートしてるよ!
と、こんな機能を提供してくれます。
インストールも簡単
gem install tod
bundlerを使っているならGemfileにtodを追加してbundle install
--path vendor/bundle
は必要なら。
bundle install # --path vebdor/bundle
もちろんいつもの感じでnew
できる
tod = Tod::TimeOfDay.new(15, 1, 1) # 15:01:01
# => #<Tod::TimeOfDay:0x007fa6b5c553d8 @hour=15, @minute=1, @second=1, @second_of_day=54061>
思い通りに時刻をパース
詳しくは公式のReadMeを読んでいただければと思いますが、こんな感じで思い通りにパースできます。
Tod::TimeOfDay.parse("8")
# => #<Tod::TimeOfDay:0x007fa6b5d34330 @hour=8, @minute=0, @second=0, @second_of_day=28800>
Tod::TimeOfDay.parse("8am")
# => #<Tod::TimeOfDay:0x007fa6b5d2d008 @hour=8, @minute=0, @second=0, @second_of_day=28800>
Tod::TimeOfDay.parse("8pm")
# => #<Tod::TimeOfDay:0x007fa6b5d1f700 @hour=20, @minute=0, @second=0, @second_of_day=72000>
Tod::TimeOfDay.parse("8p")
# => #<Tod::TimeOfDay:0x007fa6b4cd4c60 @hour=20, @minute=0, @second=0, @second_of_day=72000>
Tod::TimeOfDay.parse("9:30")
# => #<Tod::TimeOfDay:0x007fa6b4cc6390 @hour=9, @minute=30, @second=0, @second_of_day=34200>
to_s
すると分かりやすい時刻表示を取得できます。
Tod::TimeOfDay.parse("8").to_s
# => "08:00:00"
Tod::TimeOfDay.parse("8am").to_s
# => "08:00:00"
Tod::TimeOfDay.parse("8pm").to_s
# => "20:00:00"
Tod::TimeOfDay.parse("8p").to_s
# => "20:00:00"
Tod::TimeOfDay.parse("9:30").to_s
# => "09:30:00"
パースできるかわからないオブジェクトに対しても柔軟に対応
パースできればパース、できなければnilを返してくれるtry_parse
メソッド
Tod::TimeOfDay.try_parse "3:30pm"
# => #<Tod::TimeOfDay:0x007fb37c438f28 @hour=15, @minute=30, @second=0, @second_of_day=55800>
Tod::TimeOfDay.try_parse "foo"
# => nil
パースできるかチェックして、BOOLで返してくれるparsable?
メソッド
Tod::TimeOfDay.parsable? "3:30pm"
=> true
Tod::TimeOfDay.parsable? "foo"
# => false
もちろん時刻の比較や演算もできる
start_at = Tod::TimeOfDay.parse("10:00")
end_at = Tod::TimeOfDay.parse("13:00")
start_at < end_at
# => true
start_at > end_at
# => false
start_at == end_at
# => false
======= 追記 2016/5/15 =======
Timeと同じようにTod::TimeOfDayでも引き算できるようにしてみたらマージされたので追記しました。
足し算はTimeでもできないし、そもそもする場面が想像つかないのでつけませんでした。
end_at - start_at
# => #<Tod::TimeOfDay:0x007fbd49e8a9a0 @hour=3, @minute=0, @second=0, @second_of_day=10800>
いつも通りのフォーマット指定で思い通りに時刻出力
tod = Tod::TimeOfDay.parse("9:30:01")
tod.strftime("%I:%M:%S %p")
=> "09:30:01 AM"
日付情報が必要になっても簡単に情報付加できる
tod = Tod::TimeOfDay.parse("10:00")
# => #<Tod::TimeOfDay:0x007fb37c3e35c8 @hour=10, @minute=0, @second=0, @second_of_day=36000>
tod.on Date.today
# => Sat, 30 Apr 2016 10:00:00 UTC +00:00
時刻が範囲に含まれているかも簡単に確認出来る
start_at = Tod::TimeOfDay.parse("10:00")
end_at = Tod::TimeOfDay.parse("13:00")
range = Tod::Shift.new(start_at, end_at)
range.include?(Tod::TimeOfDay.parse("11:30"))
# => true
range.include?(Tod::TimeOfDay.parse("13:30"))
# => false
require 'tod/core_extensions'
すればTime
やDateTime
、Date
クラスに便利なメソッドを追加してくれる
require 'tod/core_extensions'
# => true
tod = Tod::TimeOfDay.parse("10:00")
# => #<Tod::TimeOfDay:0x007fb37c38bad0 @hour=10, @minute=0, @second=0, @second_of_day=36000>
Date.today.at tod # Dateオブジェクトに時刻情報を追加
# => Sat, 30 Apr 2016 10:00:00 UTC +00:00
Time.now.to_time_of_day # TimeオブジェクトをTimeOfDayオブジェクトに変換
# => #<Tod::TimeOfDay:0x007fb37c37b270 @hour=1, @minute=28, @second=12, @second_of_day=5292>
DateTime.now.to_time_of_day # DateTimeオブジェクトをTimeOfDayオブジェクトに変換
# => #<Tod::TimeOfDay:0x007fb37c373318 @hour=1, @minute=28, @second=12, @second_of_day=5292>
Tod::TimeOfDay
にTimeやDate、DateTimeオブジェクトを渡せばTimeOfDayオブジェクトに変換してくれる
time = Time.now
# => 2016-04-30 01:06:00 +0900
Tod::TimeOfDay(time)
# => #<Tod::TimeOfDay:0x007fb37d190c98 @hour=1, @minute=6, @second=0, @second_of_day=3960>
date = Date.today
# => Sat, 30 Apr 2016
Tod::TimeOfDay(date)
# => #<Tod::TimeOfDay:0x007fb37d180988 @hour=0, @minute=0, @second=0, @second_of_day=0>
datetime = DateTime.now
# => Sat, 30 Apr 2016 01:08:02 +0900
Tod::TimeOfDay(datetime)
# => #<Tod::TimeOfDay:0x007fb37d169878 @hour=1, @minute=8, @second=2, @second_of_day=4082>
ActiveRecordのSerialize
を使うと便利
Serialize
は好きな形式のデータをカラムに保存できるActiveRecordの機能です。
ruby, rails界隈で有名な@jnchitoさんのActiveRecord serialize / store の甘い誘惑を断ち切ろうという投稿で詳しく書かれているので是非読んでいただきたいですが、この投稿では以下の理由でSerialize
は良くないと言っています。
- シリアライズの形式が突然変更される可能性がある
- 検索ができない、ソートもできない、集計もできない、インデックスも貼れない
- データ構造やデータの内容が簡単にわからない
- 仕様変更に弱い
- 遅い
しかし、Todはシリアライズロジックを実装しているので、DBへの登録時には検索、インデックスなどしやすい文字列に変換、DBからの取得時はTod::TimeOfDayオブジェクトに変換してくれるため、yamlで登録されて検索やindexが貼れないといったような問題が発生しません。使っちゃいましょう。(この辺りの話は先ほどの記事のコメントにあります。 @jnchito さんありがとうございました。 )
class Order < ActiveRecord::Base
serialize :time, Tod::TimeOfDay
end
order = Order.create(time: Tod::TimeOfDay.new(9,30))
order.time # => 09:30:00
便利ですね!