ついにRoRの日付の闇を払った

# 時間を表現する型

## 日付（2001年1月1日）だけを扱う型
「2001年1月1日」のように**日付だけ**を表現する型を、
PostgreSQLは`DATE型`として扱える。
RoRは`Dateクラス`として扱える。

## 時間（9:05）だけを扱う型
「9時5分」のように**時間だけ**を表現できる型を、
PostgreSQLは`TIME型`として扱える。
RoRは`クラス`として存在しない。（日付をどうしても付けないといけない）


# モデルの定義とRoRのクラス
`:date`で定義したものは`Dateクラス`
`:time`で定義したものは`Timeクラス`
`:datetime`で定義したものは`ActiveSupport::TimeWithZoneクラス`
（以下`ActiveSupport::TimeWithZoneクラス`は`TimeWithZoneクラス`と呼ぶ）

## RoRで基本的に使うクラス
モデルの定義とRoR型の関係より主にRubyソース内で使うこととなるRoRの型は次の3つ。
`Dateクラス`, `Timeクラス`, `TimeWithZoneクラス`。
（この中に含まれない`DateTimeクラス`は基本的に使わない）

### TimeクラスとTimeWithZoneクラスで迷ったら
`Timeクラス`と`TimeWithZoneクラス`で迷ったら`TimeWithZoneクラス`を使っておく。


# 日付を表した文字列
## ISOで規定された表現
ISOでタイムゾーンと時間を同時に表現した書式が規定されているので、これを使う。

`2001-01-01T09:05:00+09:00` (UTC+9 日本）
`2001-01-01T09:05:00Z`(UTC+0 イギリス）

### 2001-01-01 09:05の表記
`2001-01-01 09:05`
(このようにタイムゾーンがないものをここでは `no timezone`と呼ぶこととする)

## 文字列からクラスへの変換
### 文字列 to Dateクラス
`'2000-01-01'.to_date`

### 文字列 to Timeクラス
`'2001-01-01T09:05:00+09:00'.to_time`

### 文字列 to TimeWithZoneクラス
`Time.zone.parse('2001-01-01T09:05:00+09:00')`

# クラスから年、月、日、時間、分、曜日を取り出す
ここではあえて
`config.time_zone = 'Pacific Time (US & Canada)'`
を設定した状態での動きを記載する。

## Timeクラス
```
0> hoge = '2001-01-01T09:05:00+09:00'.to_time
=> 2001-01-01 09:05:00 +0900
```

```
0> hoge.year
=> 2001
```
```
0> hoge.month
=> 1
```
```
0> hoge.day
=> 1
```
```
0> hoge.hour
=> 9
```
```
0> hoge.min
=> 5
```
```
0> hoge.wday
=> 1
```
コンフィグで日本でないタイムゾーンを設定しているのに日本の時間で`GET`している...


## TimeWithZoneクラス
```
0> foo = Time.zone.parse('2001-01-01T09:05:00+09:00')
=> Sun, 31 Dec 2000 16:05:00 PST -08:00
```
```
0> foo.year
=> 2000
```
```
0> foo.month
=> 12
```
```
0> foo.day
=> 31
```
```
0> foo.min
=> 5
```
```
0> foo.wday
=> 0
```
(あれ？　wdayでうまくとれない気がしたが気のせいか...)
コンフィグで設定した時間で取得（GET)している。

## UTCとして取り出したい
### Timeクラス
```
0> hoge = '2001-01-01T09:05:00+09:00'.to_time
=> 2001-01-01 09:05:00 +0900
```
```
0> hoge.utc
=> 2001-01-01 00:05:00 UTC
```
```
0> hoge.utc.class
=> Time
```
```
0> hoge.utc.hour
=> 0
```
`.utc`でアクセスすれば、UTCの時間としてGETできる。


### TimeWithZoneクラス
```
0> foo = Time.zone.parse('2001-01-01T09:05:00+09:00')
=> Sun, 31 Dec 2000 16:05:00 PST -08:00
```
```
0> foo.utc
=> 2001-01-01 00:05:00 UTC
```
```
0> foo.utc.class
=> Time
```
`.utc`でアクセスすれば、UTCの時間としてGETできる。
このときクラスは`Timeクラス`になっている。

## クラス to 文字列
### Timeクラス
```
0> hoge.iso8601
=> "2001-01-01T09:05:00+09:00"
```
なんで日本以外のタイムゾーン設定してるのに、+9:00で出るんだろう...

```
0> hoge.utc.iso8601
=> "2001-01-01T00:05:00Z"
```

### TimeWithZoneクラス
```
0> foo.iso8601
=> "2000-12-31T16:05:00-08:00"
```

```
0> foo.utc.iso8601
=> "2001-01-01T00:05:00Z"
```

## ハマらないために
`+9:00`がついた日本時間で文字列が取得されたり、日本時間で`.hour`が取得されたりで、ハマるので、全部`UTC`として扱ったほうがよい。

'2015-09-06T01:00:00Z'.to_time.**utc**

```
0> hoge = '2015-09-06T01:00:00Z'.to_time.utc
=> 2015-09-06 01:00:00 UTC

0> hoge.iso8601
=> "2015-09-06T01:00:00Z"
```

# クラス to 他クラス
## TimeWithZone to Time
```
0> foo = Time.zone.parse('2001-01-01T09:05:00+09:00')
=> Sun, 31 Dec 2000 16:05:00 PST -08:00
```
```
foo.utc // UTCなTimeクラスへ
```

## TimeWithZone to Date
```
0> foo = Time.zone.parse('2001-01-01T09:05:00+09:00')
=> Mon, 01 Jan 2001 09:05:00 JST +09:00

0> Date.new(foo.utc.year, foo.utc.month, foo.utc.day)
=> Mon, 01 Jan 2001
```

今回TimeやTimeWithZoneを生成する文字列に`+9:00`のものを使ったが、設計段階で、文字列もすべて`00Z`で表現できるようにしておいた方が、混乱を避けれる。

## saveするときの挙動
DBへの保存のタイムゾーンは、
`config.active_record.default_timezone`で設定。
デフォルトはUTC（イギリス）。

ということで、デフォルトで使った場合。

```
self.bar = Time.zone.parse('2001-01-01T09:05:00+09:00')
```
`self.bar`は`:datetime`で定義したもの。とした場合、DB内では、
![Screen Shot 2015-09-08 at 08.33.28.png](https://qiita-image-store.s3.amazonaws.com/0/27895/43934f0d-7190-62d6-a1bb-d435b6b1fede.png)
このように表現される。