51
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-09-07

時間を表現する型

日付(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
このように表現される。

51
46
2

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
51
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?