この記事で扱うこと
以下の情報について、覚書としてまとめました。
- migrationファイルで日付・時間に関する型を宣言した際、PostgreSQLデータベース上のテーブルの型は何になっているのか。
- PostgreSQLデータベース上でタイムゾーンはどう扱われるのか。
実行バージョンは、PostgreSQL 10.6 と Ruby on Rails 5.2.4です。
PostgreSQLに用意されている日付・時刻に関する型
PostgreSQLのドキュメントには、日付・時刻に関する型は6種類あると記載されています。中でも、
timestamp without time zone
timestamp with time zone
date
time with time zone
time without time zone
の5つがmigrationファイルの型と対応していると想像されます。これら5つとmigrationファイルの型の対応関係をまとめました。
マイグレーションを実行してみる
以下のmigrationファイルを作成し、rails db:migrate
を実行します。
class CreateSchedules < ActiveRecord::Migration[5.2]
def change
create_table :schedules do |t|
t.date :begin_at_by_date_type
t.time :begin_at_by_time_type
t.datetime :begin_at_by_datetime_type
t.timestamp :begin_at_by_timestamp_type
end
end
end
この時、PostgreSQLデータベースのテーブルのスキーマは以下です。
begin_at_by_date_type | date |
begin_at_by_time_type | time without time zone |
begin_at_by_datetime_type | timestamp without time zone |
begin_at_by_timestamp_type | timestamp without time zone |
基本的に、タイムゾーンが付かない型としてスキーマが定義されるようですね。
注意すべきなのは、migrationファイルで time
、datetime
、timestamp
型を定義した場合にはUTCに変換されて保存される一方、date
型に関してはタイムゾーンの変換が行われず、元のタイムゾーンの日付のまま保存されるという点です。
実際に実行して試してみます。例としてJSTのタイムゾーンを指定しました。
Schedule.create(
begin_at_by_date_type: "2020-01-01 06:00:00+09:00",
begin_at_by_time_type: "2020-01-01 06:00:00+09:00",
begin_at_by_datetime_type: "2020-01-01 06:00:00+09:00",
begin_at_by_timestamp_type: "2020-01-01 06:00:00+09:00"
)
これをデータベースでみると、
| begin_at_by_date_type | begin_at_by_time_type | begin_at_by_datetime_type | begin_at_by_timestamp_type
| 2020-01-01 | 21:00:00 | 2019-12-31 21:00:00 | 2019-12-31 21:00:00 |
となっています。date
型の場合のみ、JSTのタイムゾーンの日付が保存されていることがわかりますね。
タイムゾーンの情報を保持するには
設計の良し悪しについては本記事では言及しませんが、データベースに日時の値を値を保存する際はUTCに統一するケースが多いでしょう。しかし、例外的にタイムゾーンの情報も保持したい場合、次のようなmigrationファイルを作成します。
class CreateSchedules < ActiveRecord::Migration[5.2]
def change
create_table :schedules do |t|
t.date :begin_at_by_date_type
t.time :begin_at_by_time_type
t.datetime :begin_at_by_datetime_type
t.timestamp :begin_at_by_timestamp_type
t.column :begin_at_by_timestamp_with_time_zone, 'timestamp with time zone'
end
end
end
この時、PostgreSQLデータベースのテーブルのスキーマは以下です。
begin_at_by_date_type | date |
begin_at_by_time_type | time without time zone |
begin_at_by_datetime_type | timestamp without time zone |
begin_at_by_timestamp_type | timestamp without time zone |
begin_at_by_timestamp_with_time_zone | timestamp with time zone |
これで、タイムゾーン付きで日時のデータを保存することができるようになります。