PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Преобразуване между часови зони в Postgres

Нека обясня двата примера:

И в двете приемаме часова зона UTC (т.е. SET timezone TO UTC ).

db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
      timezone
---------------------
 2015-12-31 16:00:00
(1 row)

Това е еквивалентно на SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz) , т.е. Postgres имплицитно преобразува низа в timestamptz .

Знаем, че timezone функция преобразува напред и назад между timestamp и timestamptz :

Тъй като му даваме timestamptz като вход ще изведе timestamp . С други думи, той преобразува абсолютната точка във времето 2016-01-01 00:00Z до време на стена в US/Pacific , т.е. какво е показвал часовникът в Лос Анджелис в този абсолютен момент от времето.

В пример 2 правим обратното, а именно вземаме timestamp и преобразуването му в timestamptz . С други думи, ние питаме:коя беше абсолютната точка във времето, когато часовникът в Лос Анджелис показваше 2016-01-01 00:00 ?

Вие споменавате:

'2016-01-01 00:00'::timestamp е timestamp , т.е. време на стената. Той няма представа за часова зона.

Мисля, че може да не сте разбрали напълно разликата между timestamp и timestamptz , което е ключово тук. Мислете за тях като за време на стената , т.е. времето, показвано някъде по света на часовника, окачен на стената, и абсолютното време , т.е. абсолютното време в нашата вселена.

Примерите, които давате в собствения си отговор, не са съвсем точни.

SELECT ts FROM  (VALUES
(timestamptz '2012-03-05 17:00:00+0') -- outputs 2012-03-05 17:00:00+00 --1
,(timestamptz '2012-03-05 18:00:00+1') -- outputs 2012-03-05 17:00:00+00 --2
,(timestamp   '2012-03-05 18:00:00+1') -- outputs 2012-03-05 18:00:00+00 --3
,(timestamp   '2012-03-05 11:00:00'  AT TIME ZONE '+6') -- outputs 2012-03-05 17:00:00+00 --4
,(timestamp   '2012-03-05 17:00:00'  AT TIME ZONE 'UTC') -- outputs 2012-03-05 17:00:00+00 --5
,(timestamp   '2012-03-05 17:00:00'::timestamp) -- outputs 2012-03-05 17:00:00+00 --6
,(timestamp   '2012-03-05 17:00:00'::timestamptz) -- outputs 2012-03-05 17:00:00+00 --7
    ) t(ts);

Проблемът с вашия пример е, че конструирате един набор от данни с една колона. Тъй като една колона може да има само един тип, всеки ред (или отделна стойност в този случай) се преобразува в един и същи тип, а именно timestamptz , въпреки че някои стойности са изчислени като timestamp (напр. стойност 3). По този начин тук имате допълнително имплицитно преобразуване.

Нека разделим примера на отделни заявки и да видим какво се случва:

Пример 1

db=# SELECT timestamptz '2012-03-05 17:00:00+0';
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Както може би вече знаете, timestamptz '2012-03-05 17:00:00+0' и '2012-03-05 17:00:00+0'::timestamptz са еквивалентни (предпочитам второто). Затова, за да използвам същия синтаксис като в статията, ще пренапиша:

db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Сега, какво става тук? Е, по-малко, отколкото в първоначалното ви обяснение. Низът просто се анализира като timestamptz . Когато резултатът се отпечата, той използва текущо зададената timezone config, за да го конвертирате обратно в четимо от човека представяне на основната структура от данни, т.е. 2012-03-05 17:00:00+00 .

Нека променим timezone config и вижте какво се случва:

db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
      timestamptz
------------------------
 2012-03-05 18:00:00+01

Единственото нещо, което се промени, е как timestamptz се отпечатва на екран, а именно с помощта на Европа/Берлин часова зона.

Пример 2

db=# SELECT timestamptz '2012-03-05 18:00:00+1';
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Отново, просто анализирам датата.

Пример 3

db=# SELECT timestamp '2012-03-05 18:00:00+1';
      timestamp
---------------------
 2012-03-05 18:00:00
(1 row)

Това е същото като '2012-03-05 18:00:00+1'::timestamp . Това, което се случва тук е, че отместването на часовата зона просто се игнорира, защото вие искате timestamp .

Пример 4

db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Нека пренапишем, за да бъде по-просто:

db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Това пита:какво беше абсолютното време, когато часовникът на стената в часовата зона с отместване от +6 часа показваше 2012-03-05 11:00:00 ?

Пример 5

db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Нека пренапишем:

db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Това пита:какво беше абсолютното време, когато часовникът на стената в часовата зона UTC показваше 2012-03-05 17:00:00 ?

Пример 6

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

Тук предавате два пъти към timestamp , което няма разлика. Нека опростим:

db=# SELECT '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

Мисля, че е ясно.

Пример 7

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Нека пренапишем:

db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Първо анализирате низа като timestamp и след това го конвертирате в timestamptz използвайки текущо зададената timezone . Ако променим timezone , получаваме нещо друго, защото Postgres приема тази часова зона, когато преобразува timestamp (или низ без информация за часовата зона) към timestamptz :

db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+01
(1 row)

Това абсолютно време, изразено в UTC, е 2012-03-05 16:00:00+00 , като по този начин се различава от оригиналния пример.

Надявам се това да изясни нещата. Отново разбиране на разликата между timestamp и timestamptz е ключово. Помислете за времето на стената спрямо абсолютното време.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Върнете само числовите стойности от колона на PostgreSQL база данни

  2. Съвети за настройка на производителността на PostgreSQL

  3. Сравнете varchar с char

  4. ГРЕШКА:нулевата стойност в идентификатора на колона нарушава ограничението за не-нулево

  5. Тип връщане на SQL функция:TABLE срещу SETOF записи