[解決済み] RailsとPostgreSQLでタイムゾーンを完全に無視する
質問
RailsとPostgresで日付と時間を扱っているのですが、このような問題に遭遇しています。
データベースはUTCです。
Railsアプリでユーザーが任意のタイムゾーンを設定しますが、これは時間を比較するためにユーザーのローカルタイムを取得するときにのみ使用されるものです。
ユーザーは、2012年3月17日午後7時という時間を保存します。タイムゾーンの変換やタイムゾーンを保存してほしいわけではありません。ただ、その日付と時刻を保存してほしいのです。そうすれば、ユーザーがタイムゾーンを変更しても、2012年3月17日午後7時と表示されます。
私は、ユーザーが指定したタイムゾーンを、ユーザーのローカルタイムゾーンの現在時刻の「前」または「後」のレコードを取得するためにのみ使用しています。
現在、「タイムゾーンなしのタイムスタンプ」を使っていますが、レコードを取得する際に、rails(?)がアプリ内のタイムゾーンに変換してしまうので、これは困りますね。
Appointment.first.time
=> Fri, 02 Mar 2012 19:00:00 UTC +00:00
データベース内のレコードはUTCで出力されるようなので、私のハックは、現在時刻を取得し、タイムゾーンを削除するために 'Date.strptime(str, "%m/%d/%Y")' とし、それを使ってクエリーを実行することです。
.where("time >= ?", date_start)
もっと簡単にタイムゾーンを無視する方法がありそうな気がするのですが。何かアイデアはありますか?
解決方法は?
Postgresには2つの異なるタイムスタンプデータタイプがあります。
-
timestamp with time zone
、略称。timestamptz
-
timestamp without time zone
、略称。timestamp
timestamptz
は
好ましい
は、文字通り日付/時刻の系列に属する型です。この型には
typispreferred
に設定されています。
pg_type
というように、関連性を持たせることができます。
内部ストレージと エポック
内部的には、タイムスタンプは占有している 8バイト ディスクとRAMに保存されます。これは、Postgresのエポック、2000-01-01 00:00:00 UTCからのマイクロ秒のカウントを表す整数値です。
Postgresはまた、一般的に使用されている
UNIX時間
UNIX のエポック 1970-01-01 00:00:00 UTC からの秒数をカウントしており、これを関数
to_timestamp(double precision)
または
EXTRACT(EPOCH FROM timestamptz)
.
* タイムスタンプ、およびインターバルの h/m/s フィールドは、以下のように格納されます。 * マイクロ秒を単位とするint64値。 (昔は * 秒単位のdouble値)
そして
/* UnixとPostgresの計算におけるユリウス暦の0日に相当するもの */ #define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
マイクロ秒の分解能は、秒の小数点以下の桁数が最大6桁になるように変換されます。
timestamp
について
timestamp
タイムゾーンが明示的に指定されていません。ポストグレス
無視
入力リテラルに誤って追加されたタイムゾーン修飾子がある場合
時間をずらして表示することはありません。すべてが同じタイムゾーンで起きている場合、これは問題ありません。異なるタイムゾーンの場合は 意味 が変わりますが 価値 と 表示 はそのままです。
timestamptz
の取り扱いについて
timestamptz
が微妙に違う。
ここでマニュアルを引用します
:
について
timestamp with time zone
の場合、内部で保存されている値は
常にUTCで
(万国標準時)
太字の強調は私です。その
タイムゾーンそのものが保存されることはない
. これは、UTC タイムスタンプを計算するための入力修飾子であり、 タイムゾーンオフセットを付加して保存したり、 表示用のローカルタイムを計算するための出力装飾子でもあります。オフセットを付加しない場合は
timestamptz
を入力すると、セッションの現在のタイムゾーン設定が想定されます。すべての計算は、UTCタイムスタンプ値で行われます。複数のタイムゾーンを扱う必要がある (かもしれない) 場合は、以下のようにします。
timestamptz
. 言い換えれば もし、想定されるタイムゾーンに疑問や誤解がある場合は
timestamptz
. ほとんどのユースケースで適用されます。
psql や pgAdmin などのクライアント、あるいは libpq (Ruby の pg gem など) は、タイムスタンプと 現在のタイムゾーン または 要求された タイムゾーン(下記参照)。それは常に 同一時刻 表示形式が異なるだけです。あるいは マニュアルにあるように :
<ブロッククオート
タイムゾーンを考慮した日付と時刻は、すべて内部的にUTCで保存されます。それらは
で指定されたゾーンのローカルタイムに変換されます。
TimeZone
の設定パラメータを使用して、クライアントに表示されます。
psqlでの例。
db=# SELECT timestamptz '2012-03-05 20:00+03';
timestamptz
------------------------
2012-03-05 18:00:00+01
ここで何が起こったのか?
任意のタイムゾーンオフセットを選択した
+3
を入力リテラルに指定します。Postgresにとって、これはUTCタイムスタンプを入力するための多くの方法のうちの1つに過ぎません。
2012-03-05 17:00:00
. クエリの結果は
表示
現在のタイムゾーン設定に対して
ウィーン/オーストリア
私のテストでは、オフセットを持つ
+1
冬期と
+2
は、夏時間(サマータイム)です。そのため
2012-03-05 18:00:00+01
DSTが始まるのが遅いため。
Postgresは入力リテラルをすぐに忘れてしまいます。覚えているのはデータ型に対応する値だけです。ちょうど10進数の場合のように。
numeric '003.4'
または
numeric '+3.4'
- は、どちらも全く同じ内部値になります。
AT TIME ZONE
あとは、タイムスタンプリテラルを特定のタイムゾーンにしたがって解釈したり表現したりするツールがあればよいのです。そこで
AT TIME ZONE
の構成が入ります。2つの異なる使用例があります。
timestamptz
が変換され
timestamp
であり、その逆もまた然りである。
UTCを入力する場合
timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
...と同等である。
SELECT timestamptz '2012-03-05 17:00:00 UTC'
ESTと同じ時点を表示する場合
timestamp
(東部標準時)を表示します。
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
その通りです。
AT TIME ZONE 'UTC'
2回
. 1つ目の解釈は
timestamp
の値を (与えられた) UTC タイムスタンプとして返します。
timestamptz
. 2 番目の変換は
timestamptz
を
timestamp
は、指定されたタイムゾーン 'EST' で、壁掛け時計がこの時点のタイムゾーン EST で表示するものです。
例
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
8(または9)を返します。
同一
同じUTCタイムスタンプを持つtimestamptzカラムの行を持つ
2012-03-05 17:00:00
. 9行目は、私のタイムゾーンではたまたまうまくいくのですが、邪悪な罠です。下記をご覧ください。
6行目~8行目(タイムゾーンあり
名称
およびタイムゾーン
略語
は、DST(サマータイム)の影響を受けるため、異なる場合があります(現在はありません)。のようなタイムゾーン名は
'US/Hawaii'
のような略語は、夏時間の規則とすべての歴史的なシフトを自動的に認識します。
HST
は、固定オフセットのための単なるダムコードです。夏時間と標準時間には、別の略語を追加する必要があるかもしれません。その
名前
を正しく解釈します。
任意
のタイムスタンプを指定されたタイムゾーンで表示します。また
省略形
は安価ですが、与えられたタイムスタンプに対して正しいものである必要があります。
<サブ サマータイムは、人類が考え出した最も優れたアイデアのひとつではありません。
9行目、マーク
ロードフットガン
作品
私の場合
しかし、それは偶然に過ぎません。もし、明示的にリテラルを
timestamp [without time zone]
,
タイムゾーンのオフセットは無視されます
! 素のタイムスタンプのみが使用される。この値は自動的に
timestamptz
の例では、カラムの型に合わせるために このステップでは
timezone
の設定は、現在のセッションと同じタイムゾーンであると仮定します。
+1
は、私の場合(ヨーロッパ/ウィーン)。しかし、おそらくあなたの場合はそうではないでしょう - 違う値になります。要するに
timestamptz
リテラルを
timestamp
を使用すると、タイムゾーンのオフセットが失われます。
ご質問内容
<ブロッククオートユーザーは、2012年3月17日午後7時というように時間を保存します。私は、タイムゾーン の変換やタイムゾーンが保存されます。
タイムゾーンそのものが保存されることはありません。UTCタイムスタンプを入力するには、上記の方法のいずれかを使用してください。
<ブロッククオート私は、ユーザーが指定したタイムゾーンを使用して、「前」または「後」のレコードを取得するだけです。 ユーザーのローカルタイムゾーンでの現在時刻より後。
異なるタイムゾーンのすべてのクライアントに1つのクエリを使用することができます。
グローバルな絶対時刻の場合。
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
ローカルクロックによる時刻の場合。
SELECT * FROM tbl WHERE time_col > now()::time
背景の情報にはまだ飽きてない? マニュアルにはもっとたくさん載っています。
関連
-
[解決済み】TypeError: SymbolからIntegerへの暗黙の変換がない。
-
[解決済み] デバイスを使用する現在のユーザーメソッド
-
[解決済み] railsで':remote => true'はどのように動作するのでしょうか?
-
[解決済み] Heroku: 既存のrailsアプリにseeds.rbをプッシュする方法?
-
[解決済み] レイル 4 radio_button_tag default not selected
-
[解決済み] rails/rubyでgroup_byを使用する。
-
[解決済み] Rails 4で、以前のバージョンのRailsでattr_accessibleを使用していた状況に遭遇した場合、Forbidden Attributes Errorが発生する。
-
[解決済み] JavaScriptでクライアントのタイムゾーン(およびオフセット)を取得する
-
[解決済み】特定のタイムゾーンで日付をフォーマットする
-
[解決済み】UTCの日付時間の文字列をローカルの日付時間に変換する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Bundler: コマンドが見つからない
-
[解決済み] サーバーに接続できませんでした。そのようなファイルまたはディレクトリがありません (PG::ConnectionBad)
-
[解決済み】PG::ConnectionBad: fe_sendauth: パスワードが供給されない
-
[解決済み] nil:NilClass の未定義メソッド `each'... なぜでしょうか?
-
[解決済み] Ruby/Rails の「フック」とは何ですか?
-
[解決済み] デバイスを使用する現在のユーザーメソッド
-
[解決済み] どなたか、collection_selectをわかりやすく説明していただけませんか?
-
[解決済み] Ruby: public static メソッドを作るには?
-
[解決済み] RSpecとCucumberの違いは何ですか?[クローズド]
-
[解決済み】PostgreSQLでタイムゾーンがある場合とない場合のタイムスタンプの違いについて