rubyで時刻だけのオブジェクトを扱いたい。そんな時は多機能なgem todを使おう。

ちょっとテンションが上がってしまったので[Tod](https://github.com/jackc/tod)についてまとめました。
# 多機能な`Tod::TimeOfDay`
`tod`をインストールすると`Tod::TimeOfDay`クラスが使えるようになります。

* 時刻のパース
* 比較
* フォーマット出力
* 演算
* その他ちょろちょろ
* Railsのシリアライズもサポートしてるよ！

と、こんな機能を提供してくれます。

# インストールも簡単
```rb
gem install tod
```
bundlerを使っているならGemfileにtodを追加してbundle install
`--path vendor/bundle`は必要なら。

```rb
bundle install # --path vebdor/bundle
```
# もちろんいつもの感じで`new`できる
```rb
tod = Tod::TimeOfDay.new(15, 1, 1) # 15:01:01
# => #<Tod::TimeOfDay:0x007fa6b5c553d8 @hour=15, @minute=1, @second=1, @second_of_day=54061>
```

# 思い通りに時刻をパース
詳しくは公式のReadMeを読んでいただければと思いますが、こんな感じで思い通りにパースできます。

```rb
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`すると分かりやすい時刻表示を取得できます。

```rb
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`メソッド
```rb
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?`メソッド
```rb
Tod::TimeOfDay.parsable? "3:30pm"
=> true
Tod::TimeOfDay.parsable? "foo"
# => false
```

# もちろん時刻の比較や演算もできる
```rb
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でも引き算できるようにしてみたら[マージされた](https://github.com/jackc/tod/pull/35)ので追記しました。
足し算はTimeでもできないし、そもそもする場面が想像つかないのでつけませんでした。

```rb
end_at - start_at
# => #<Tod::TimeOfDay:0x007fbd49e8a9a0 @hour=3, @minute=0, @second=0, @second_of_day=10800>
```

# いつも通りのフォーマット指定で思い通りに時刻出力
```rb
tod = Tod::TimeOfDay.parse("9:30:01")
tod.strftime("%I:%M:%S %p")
=> "09:30:01 AM"
```

# 日付情報が必要になっても簡単に情報付加できる
```rb
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
```

# 時刻が範囲に含まれているかも簡単に確認出来る
```rb
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`クラスに便利なメソッドを追加してくれる

```rb
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オブジェクトに変換してくれる
```rb
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 の甘い誘惑を断ち切ろう](http://qiita.com/jnchito/items/68e91e9bf46f960a79e4)という投稿で詳しく書かれているので是非読んでいただきたいですが、この投稿では以下の理由で`Serialize`は良くないと言っています。

1. シリアライズの形式が突然変更される可能性がある
1. 検索ができない、ソートもできない、集計もできない、インデックスも貼れない
1. データ構造やデータの内容が簡単にわからない
1. 仕様変更に弱い
1. 遅い

しかし、Todはシリアライズロジックを実装しているので、DBへの登録時には検索、インデックスなどしやすい文字列に変換、DBからの取得時はTod::TimeOfDayオブジェクトに変換してくれるため、yamlで登録されて検索やindexが貼れないといったような問題が発生しません。使っちゃいましょう。（この辺りの話は[先ほどの記事のコメント](http://qiita.com/jnchito/items/68e91e9bf46f960a79e4#comment-8f0bf489d14bdb72d188)にあります。 @jnchito さんありがとうございました。 ）

```rb
class Order < ActiveRecord::Base
  serialize :time, Tod::TimeOfDay
end
order = Order.create(time: Tod::TimeOfDay.new(9,30))
order.time                                      # => 09:30:00
```

便利ですね！