14

クロックアラームPostgresテーブルがあります(実際にはそうではありませんが、これは類似しており、説明が簡単です)。アラームは1時間の解像度でユーザーによって設定され、ユーザーはさまざまなタイムゾーンのユーザーである可能性があります。アラームは毎日繰り返されています。1日の特定の時間に鳴るはずのアラームを確実に取得したいのですが、夏時間に問題があります。どうすればこれを最善の方法で行うことができますか?

アルフレッドとロッタはどちらもストックホルムに住んでいます(UTCから+1時間、DSTの場合は+ 2時間)。
シャロンはシンガポールに住んでいます(UTCから+8時間、DSTなし)

冬の間、アルフレッドは午前4時にアラームを設定します。アラームは、一年中現地時間の午前4時に鳴るはずです。
夏の間、ロッタは午前5時にアラームを設定します。繰り返しになりますが、一年中午前5時にオフになります。
その間、シャロンは午前11時にアラームを設定しました。

これらはすべて、03:00UTCとしてデータベースに保存できます。

冬にUTC03:00に鳴るアラームをデータベースに照会する場合、アルフレッドとシャロンのアラームが必要です。シンガポールは現在スウェーデンから+7時間なので、シンガポールの午前11時はスウェーデンの午前4時です。ロッタのアラームはもう1時間鳴らないはずです。

逆に、夏にUTC 03:00に鳴るアラームをデータベースに照会する場合は、ロッタとシャロンのアラームが必要です。シンガポールは現在スウェーデンから+6時間なので、シンガポールの午前11時はスウェーデンの午前5時です。スヴェンのアラームは1時間前に鳴りました。

これを保存してデータベースにクエリを実行するにはどうすればよいですか?

必要に応じてdbスキーマを変更できます。現時点では、DSTはまったく調整されておらず、実際には「時間」整数フィールドがあります(これはばかげているようですが、時間フィールドの方が適しています)。

UTC時間とタイムゾーン情報の両方を保存する必要があるようですが、Postgresでこれを最もよく達成する方法がわかりません。Postgresにはある種のタイムゾーンの概念があることがわかりましたが、私が知る限り、タイムゾーンフィールドタイプはありません。また、タイムゾーンデータと作成日に基づいて、選択でUTC時間をオフセットする方法を決定するために、SQLでいくつかの計算を行う必要があると思います。私はSQLが苦手です…</p>

多くの「アラーム」が存在する可能性があるため、Postgresでこれを解決したいと思います。また、それらすべてをRubyにフェッチしてそこでフィルタリングすることに伴うパフォーマンスの問題を回避したいと思います。(はい、これはRailsアプリです。)

4

3 に答える 3

14

timestamp with time zone計算には(timestamptz)を使用します。
アラームの時間はtime [without time zone]。ただし、行ごとにタイムゾーン
を明示的 に保存する必要があります。

絶対に使用しないでくださいtime with time zonetimetz)これは論理的に壊れたタイプであり、PostgreSQLでは使用をお勧めしません。マニュアル:

タイプtime with time zoneはSQL標準で定義されていますが、定義には疑わしい有用性につながるプロパティが示されています。ほとんどの場合、、、、の組み合わせはdate、アプリケーションtimeに必要な日付/時刻機能の完全な範囲を提供する必要がありますtimestamp without timezonetimestamp with time zone

デモのセットアップ:

CREATE TABLE alarm(name text, t time, tz text);
INSERT INTO alarm VALUES
  ('Alfred', '04:00', 'Europe/Stockholm') -- Alfred sets an alarm for 4 AM.
, ('Lotta',  '05:00', 'Europe/Stockholm') -- Lotta sets an alarm for 5 AM. 
, ('Sharon', '11:00', 'Asia/Singapore');  -- Sharon has set an alarm for 11 AM.

DSTを説明するには、タイムゾーン(省略形ではない)である必要があります。関連している:

「今日」に一致するアラームを取得します。

SELECT *
FROM   alarm
WHERE  (('2012-07-01'::date + t) AT TIME ZONE tz AT TIME ZONE 'UTC')::time
       = '03:00'::time
  • ('2012-7-1'::date + t)...アセンブルも「今日」のためtimestamp [without time zone] だけかもしれません。now()::date + t
  • AT WITH TIME ZONE tz...タイムスタンプを保存されたタイムゾーンに配置すると、結果はtimestamptz
  • AT WITH TIME ZONE 'UTC'...UTCに従って取得timestamp
  • ::time...時間コンポーネントを抽出する最も簡単な方法。

ここでは、タイムゾーン名を検索できます。

SELECT *
FROM   pg_timezone_names
WHERE  name ~~* '%sing%'
LIMIT  10;

db <> fiddlehere-夏/冬のデモンストレーション
古いsqlfiddle

于 2012-11-06T01:16:46.300 に答える
7

これを行うには、EDT/ESTではなくAmerica/New_Yorkなどの完全なタイムゾーン名を使用し、UTCではなくそのタイムゾーンに時間を保存します。その後、夏時間のオフセットの変更を幸いにも知らないままにすることができます。

次のようなものが機能するはずです。

-- CREATE TABLE time_test (
--   user_to_alert CHARACTER VARYING (30),
--   alarm_hour TIME,
--   user_timezone CHARACTER VARYING (30)
-- );

SELECT user_to_alert, 
  CASE
    WHEN EXTRACT(HOUR FROM CURRENT_TIME AT TIME ZONE user_timezone) = EXTRACT(HOUR FROM alarm_hour) THEN TRUE
  ELSE FALSE
END AS raise_alarm
FROM time_test;

または:

SELECT user_to_alert
FROM time_test
WHERE EXTRACT(HOUR FROM CURRENT_TIME AT TIME ZONE user_timezone) = EXTRACT(HOUR FROM alarm_hour);
于 2012-11-06T00:56:27.917 に答える
2

与えられた:

SET timezone = 'UTC';

CREATE TABLE tzdemo (
    username text not null,
    alarm_time_utc time not null,
    alarm_tz_abbrev text not null,
    alarm_tz text not null
);

INSERT INTO tzdemo (username, alarm_time_utc, alarm_tz_abbrev, alarm_tz) VALUES
('Alfred', TIME '04:00' AT TIME ZONE '+01:00', 'CET', 'Europe/Stockholm'),
('Lotta', TIME '05:00' AT TIME ZONE '+02:00', 'CEST', 'Europe/Stockholm'),
('Sharon', TIME '11:00' AT TIME ZONE '+08:00', 'SGT', 'Singapore');

試す:

SELECT username 
FROM tzdemo 
WHERE alarm_time_utc AT TIME ZONE alarm_tz_abbrev = TIME '03:00' AT TIME ZONE alarm_tz;

結果:

 username 
----------
 Alfred
 Sharon
(2 rows)

原理:

  • アラームが作成されたタイムゾーンオフセットを、その時点でDSTであったかどうかを含めて保存します
  • UTCに変換された時刻も保存します
  • クエリを実行するときは、完全なタイムゾーン名が時刻の現在のUTC規則に従うという事実を使用して、リージョンの現在のタイムゾーンにある時刻を生成します。そのアラームが作成されたときのタイムゾーンに保存されているタイムスタンプと比較します。

これにより、ユーザーが場所を変更してタイムゾーンを変更した場合にも対処できます。

このアプローチは、「特定の場所でアラームが鳴る現地時間」など、予測クエリを実行するときにタイムスタンプを日付修飾することで拡張できます。

私はこの解決策に完全に自信がないので、注意深くテストすることをお勧めします。

于 2012-11-06T01:11:21.157 に答える