2016-08-09 1 views
0

Was ist die beste Möglichkeit, einem Zeitstempel mit Zeitzone ein spezifiziertes Intervall hinzuzufügen, wenn ich die Berechnung nicht in der Zeitzone des Servers durchführen möchte . Dies ist besonders wichtig bei der Umstellung auf Sommerzeit.PostgresSQL: Hinzufügen eines Intervalls zu einem Zeitstempel in einer anderen Zeitzone

z.B. betrachte den Abend, an dem wir "aufspringen". (Hier in Toronto, ich glaube, es war 2016-03-13 um 2 Uhr morgens).
Wenn ich einen Zeitstempel nehmen: 2016-03-13 00:00:00-05 und füge '1 day' es, in Canada/Eastern, würde ich erwarten, 2016-03-14 00:00:00-04 zu bekommen - später> 1 Tag, aber eigentlich nur 23 Stunden Aber wenn ich hinzufügen, 1 Tag, um es in Saskatchewan (ein Ort, der nicht DST verwendet), würde ich wollen, dass es 24 Stunden hinzufügen, so dass ich mit 2016-03-13 01:00:00-04 enden würde.

Wenn ich Spalten/Variablen

t1 timestamp with time zone; 
t2 timestamp with time zone; 
step interval; 
zoneid text; --represents the time zone 

ich im Wesentlichen

t2 = t1 + step; --in a time zone of my choosing 

Postgres Dokumentation sagen will, dass der Zeitstempel mit Zeitzone, um anzuzeigen, scheint intern in UTC-Zeit gespeichert, die um anzuzeigen, scheinen dass eine Timestamptz-Spalte keine Berechnung einer Zeitzone enthält. Der SQL-Standard gibt an, dass die Operation datetime + interval die Zeitzone des ersten Operanden beibehalten soll.

t2 = (t1 AT TIME ZONE zoneid + step) AT TIME ZONE zoneid; 

nicht zu funktionieren scheint, weil der erste Guss t1 in eine Zeitzone weniger Zeitstempel verwandelt und somit nicht DST

t2 = t1 + step; 

scheint nicht zu arbeiten, wie es die Übergänge rechnen können tut Betrieb in der Zeitzone meines SQL-Servers

Legen Sie die Postgres-Zeitzone vor der Operation und ändern Sie sie wieder nach?

Eine bessere Darstellung:

CREATE TABLE timestamps (t1 timestamp with time zone, timelocation text); 
SET Timezone 'America/Toronto'; 
INSERT INTO timestamps(t1, timelocation) VALUES('2016-03-13 00:00:00 America/Toronto', 'America/Toronto'); 
INSERT INTO timestamps(t1, timelocation) VALUES('2016-03-13 00:00:00 America/Regina', 'America/Regina'); 

SELECT t1, timelocation FROM timestamps; -- shows times formatted in Toronto time. OK 
"2016-03-13 00:00:00-05";"America/Toronto" 
"2016-03-13 01:00:00-05";"America/Regina" 

SELECT t1 + '1 day', timelocation FROM timestamps; -- Toronto timestamp has advanced by 23 hours. OK. Regina time stamp has also advanced by 23 hours. NOT OK. 
"2016-03-14 00:00:00-04";"America/Toronto" 
"2016-03-14 01:00:00-04";"America/Regina" 

Wie dies umgehen?

a) Werfen Sie die Zeitmarke auf einen Zeitstempel tz in der entsprechenden Zeitzone?

SELECT t1 AT TIME ZONE timelocation + '1 day', timelocation FROM timestamps; --OK. Though my results are timestamps without time zone now. 
"2016-03-14 00:00:00";"America/Toronto" 
"2016-03-14 00:00:00";"America/Regina" 

SELECT t1 AT TIME ZONE timelocation + '4 hours', timelocation FROM timestamps; -- NOT OK. I want the Toronto time to be 5am 
"2016-03-13 04:00:00";"America/Toronto" 
"2016-03-13 04:00:00";"America/Regina" 

b) Änderung der Zeitzone der Postgres und fortzufahren.

Diese Lösung funktioniert nur, wenn ich die Postgres-Zeitzone vor jedem Betriebszeitintervall kontinuierlich umschalte.

+0

Edit: Die Variablen t1 und t2 sollten als "Zeitstempel mit Zeitzone" geschrieben worden sein. Entschuldigung, ich bin neu hier und kann die Bearbeitungsschaltfläche nicht finden. – djmorris

Antwort

2

Es ist eine Kombination von zwei Eigenschaften, die Ihr Problem verursacht:

  1. timestamp with time zone in UTC gespeichert und enthält keine Zeitzoneninformationen. Ein besserer Name dafür wäre “ UTC Timestamp ”.

  2. Zugabe von timestamp with time zoneinterval und wird immer in der aktuellen Zeitzone durchgeführt wird, das heißt, die einen Satz mit dem Konfigurationsparameter TimeZone.

Da das, was Sie wirklich speichern müssen, ist ein Zeitstempel und die Zeitzone, in der es gültig ist, sollten Sie eine Kombination aus timestamp without time zone speichern und ein text, die die Zeitzone.

Wie Sie richtig bemerkt haben, müssten Sie die aktuelle Zeitzone ändern, um eine Intervalladdition über die Sommerzeit korrekt durchzuführen (sonst weiß PostgreSQL nicht, wie lange 1 day ist). Aber Sie müssen nicht, dass von Hand zu tun, können Sie eine verwenden PL/pgSQL Funktion es für Sie zu tun:

CREATE OR REPLACE FUNCTION add_in_timezone(
     ts timestamp without time zone, 
     tz text, 
     delta interval 
    ) RETURNS timestamp without time zone 
    LANGUAGE plpgsql IMMUTABLE AS 
$$DECLARE 
    result timestamp without time zone; 
    oldtz text := current_setting('TimeZone'); 
BEGIN 
    PERFORM set_config('TimeZone', tz, true); 
    result := (ts AT TIME ZONE tz) + delta; 
    PERFORM set_config('TimeZone', oldtz, true); 
    RETURN result; 
END;$$; 

dass Sie die folgenden geben würde, wo das Ergebnis in das zu verstehen ist, gleichen Zeitzone wie das Argument:

test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Toronto', '1 day'); 
    add_in_timezone 
--------------------- 
2016-03-14 00:00:00 
(1 row) 

test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Regina', '1 day'); 
    add_in_timezone 
--------------------- 
2016-03-14 00:00:00 
(1 row) 

test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Toronto', '4 hours'); 
    add_in_timezone 
--------------------- 
2016-03-13 05:00:00 
(1 row) 

test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Regina', '4 hours'); 
    add_in_timezone 
--------------------- 
2016-03-13 04:00:00 
(1 row) 

Sie in Erwägung ziehen könnte eine kombinierte Art

CREATE TYPE timestampattz AS (
    ts timestamp without time zone, 
    zone text 
); 

und definieren Operatoren Erstellen und wirft es auf, aber das ist wahrscheinlich ein Großprojekt, das übersteigt, was Sie dafür wollen.

Es gibt sogar eine PostgreSQL-Erweiterung timestampandtz, die genau das tut; Vielleicht ist es genau das, was du brauchst (ich habe nicht gesehen, was die Semantik für die Addition ist).

+0

Danke Laurenz. Leider habe ich meine Frage übertippt. Die zwei Variablen sind in der Tat Zeitstempel mit Zeitzone, wie Sie vorgeschlagen haben. Aber das Problem scheint immer noch zu bestehen. – djmorris

+0

Beweisen Sie es bitte. Wie unterscheidet sich Ihr Anwendungsfall von den Beispielen, die ich angegeben habe? –

+0

Anstelle von TIMESTAMPTZ '2016-03-13 00:00:00 CST' fügen Sie diesen Wert zusammen mit TIMESTAMPTZ '2016-03-13 00:00:00 America/Toronto' in eine Tabelle mit einer Timestamptz-Spalte ein. Dann brauche ich eine Methode, um '1 Tag' entweder einem "CST" -Zeitrahmen oder einem "Toronto" -Zeitrahmen hinzuzufügen. – djmorris

Verwandte Themen