Изглежда, че искате да съхраните местно време по отношение на определена часова зона. В този случай запазете timestamp
(без часова зона) и timezone
в отделна колона.
Да предположим например, че искате да запишете събитие, което ще се случи в 10 сутринта на 26 февруари 2030 г. в Чикаго и трябва да е в 10 сутринта местно време независимо от правилото за часовата зона в сила на тази дата.
Ако базата данни съхранява клеймото за време без часова зона:
unutbu=# select '2030-02-26 10:00:00'::timestamp as localtime, 'America/Chicago' AS tzone;
+---------------------+-----------------+
| localtime | tzone |
+---------------------+-----------------+
| 2030-02-26 10:00:00 | America/Chicago |
+---------------------+-----------------+
След това по-късно можете да намерите UTC датата и часа на събитието, като използвате
unutbu=# select '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 16:00:00 |
+---------------------+
Заявката връща UTC дата и час, 2030-02-26 16:00:00
, което съответства на 2030-02-26 10:00:00
местно време в Чикаго.
Използване на AT TIME ZONE
забавя прилагането на правилата за часовата зона до кога е направена заявката вместо кога timestamptz
беше вмъкнато.
Използване на AT TIME ZONE
на timestamp
локализира датата и часа към дадената часова зона, но отчита датата и часът в часовата зона на потребителя .Използване на AT TIME ZONE
на timestamptz
преобразува datetime в дадената часова зона, след което премахва отместването, като по този начин връща timestamp
.По-горе, AT TIME ZONE
се използва два пъти:първо за локализиране на timestamp
и следващ за преобразуване на върнатия timestamptz
към нова часова зона (UTC). Резултатът е timestamp
в UTC.
Ето пример, демонстриращ AT TIME ZONE
поведението на timestamp
s:
unutbu=# SET timezone = 'America/Chicago';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 10:00:00-06 |
+------------------------+
unutbu=# SET timezone = 'America/Los_Angeles';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 08:00:00-08 |
+------------------------+
2030-02-26 10:00:00-06
и 2030-02-26 08:00:00-08
са едни и същи дати и часове, но са отчетени в различни потребителски часови зони. Това показва, че 10 сутринта в Чикаго е 8 сутринта в Лос Анджелис (използвайки текущите дефиниции на часовата зона):
unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Алтернатива на използването на AT TIME ZONE
два пъти е задаване на потребителската часова зона
до UTC
. След това можете да използвате
select localtime AT TIME ZONE tzone
Имайте предвид, че когато се прави по този начин, timestamptz
се връща вместо timestamp
.
Имайте предвид, че съхраняването на местни часове може да бъде проблематично, тъй като може да има несъществуващи часове и двусмислени времена. Например 2018-03-11 02:30:00
е несъществуващо местно време в America/Chicago
. Postgresql нормализира несъществуващи местни времена, като приема, че се отнася за съответния час след началото на лятното часово време (DST) (сякаш някой е забравил да настрои часовника си напред):
unutbu=# select '2018-03-11 02:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
unutbu=# select '2018-03-11 03:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
Пример за двусмислено местно време е 2018-11-04 01:00:00
в America/Chicago
. Среща се два пъти поради лятното часово време. Postgresql разрешава тази неяснота, като избира по-късния час, след края на лятното часово време:
unutbu=# select '2018-11-04 01:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 07:00:00 |
+---------------------+
Обърнете внимание, че това означава, че няма начин да се препрати към 2018-11-04 06:00:00 UTC
чрез съхраняване на местно време в America/Chicago
часова зона:
unutbu=# select '2018-11-04 00:59:59'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 05:59:59 |
+---------------------+