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

Изцяло игнориране на часовите зони в Rails и PostgreSQL

Postgres има два различни типа данни за времеви печати:

  • timestamp with time zone , кратко име:timestamptz
  • timestamp without time zone , кратко име:timestamp

timestamptz е предпочитаният въведете семейството дата/час, буквално. Има typispreferred зададен в pg_type , което може да е от значение:

  • Генериране на времеви серии между две дати в PostgreSQL

Вътрешно хранилище и епоха

Вътрешно времевите марки заемат 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) .

Изходният код:

* Timestamps, as well as the h/m/s fields of intervals, are stored as
* int64 values with units of microseconds.  (Once upon a time they were  
* double values with units of seconds.)

И:

/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */  
#define UNIX_EPOCH_JDATE        2440588 /* == date2j(1970, 1, 1) */  
#define POSTGRES_EPOCH_JDATE    2451545 /* == date2j(2000, 1, 1) */  

Микросекундната разделителна способност се превежда до максимум 6 дробни цифри за секунди.

timestamp

За timestamp не е посочена изрично часова зона. Postgres игнорира всеки модификатор на часовата зона, добавен към входния литерал по погрешка!

Няма изместване на часове за показване. С всичко, което се случва в една и съща часова зона, това е добре. За различна часова зона значението промени, но стойност и дисплей останете същите.

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 времеви печат 2012-03-05 17:00:00 . Резултатът от заявката се показва за текущата настройка на часовата зона Виена/Австрия в моя тест, който има отместване +1 през зимата и +2 през лятното часово време ("лятно часово време", DST). Така че 2012-03-05 18:00:00+01 тъй като DST започва само по-късно.

Postgres веднага забравя входния литерал. Всичко, което помни, е стойността за типа данни. Точно както с десетичното число. numeric '003.4' или numeric '+3.4' - и двете водят до една и съща вътрешна стойност.

AT TIME ZONE

Всичко, което липсва сега, е инструмент за интерпретиране или представяне на литерали за времеви отпечатъци според конкретна часова зона. Това е мястото, където AT TIME ZONE construct идва. Има два различни случая на употреба. 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' два пъти . Първият интерпретира timestamp стойност като (предоставена) UTC времеви печат, връщащ типа timestamptz . Вторият преобразува 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 2012-03-05 17:00:00 . 9-ти ред изглежда работи в моята часова зона, но е зъл капан. Вижте по-долу.

① Редове 6 - 8 с име на часовата зона и съкращение на часовата зона за Хавайското време са обект на DST (лятно часово време) и може да се различават, макар и не в момента. Име на часовата зона като 'US/Hawaii' е наясно с правилата за DST и всички исторически смени автоматично, докато съкращение като HST е просто тъп код за фиксирано изместване. Може да се наложи да добавите различно съкращение за лятно/стандартно време. Името правилно интерпретира всяко времева марка в дадена часова зона. Съкращение е евтин, но трябва да е правилният за дадената времева марка:

  • Имена на часови зони с идентични свойства дават различен резултат, когато се прилагат към времеви печат

Лятното часово време не е сред най-ярките идеи, които човечеството някога е измисляло.

② Ред 9, маркиран като зареден крак работи при мен , но само по стечение на обстоятелствата. Ако изрично предадете литерал към timestamp [without time zone] , всяко изместване на часовата зона се игнорира! Използва се само голата времева марка. След това стойността автоматично се принуждава към timestamptz в примера, за да съответства на типа колона. За тази стъпка, timezone се приема настройката на текущата сесия, която се оказва в същата часова зона +1 в моя случай (Европа/Виена). Но вероятно не във вашия случай - което ще доведе до различна стойност. Накратко:Не предавайте timestamptz литерали към timestamp или губите изместването на часовата зона.

Вашите въпроси

Потребителят съхранява време, да речем 17 март 2012 г., 19:00 ч. Не искам преобразуванията на часовата зона или часовата зона да се съхраняват.

Самата часова зона никога не се съхранява. Използвайте един от методите по-горе, за да въведете времева марка за UTC.

Използвам само посочената от потребителите часова зона, за да получавам записи „преди“ или „след“ текущото време в местната часова зона на потребителите.

Можете да използвате една заявка за всички клиенти в различни часови зони.
За абсолютно глобално време:

SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time

За времето според местния часовник:

SELECT * FROM tbl WHERE time_col > now()::time

Все още не сте уморени от основна информация? Има още в ръководството.



  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 v13 Разгръщане и мащабиране с ClusterControl 1.8.2

  3. Не може да се свърже с postgres с помощта на jdbc в pyspark shell

  4. Вземете деня от дата в PostgreSQL

  5. Rails:Отказано е разрешение на Postgres за създаване на база данни на rake db:create:all