sql >> Database teknologi >  >> RDS >> PostgreSQL

Hvilken tidsstempeltype skal jeg vælge i en PostgreSQL-database?

For det første er PostgreSQLs tidshåndtering og aritmetik fantastisk, og mulighed 3 er fin i det generelle tilfælde. Det er dog en ufuldstændig visning af tid og tidszoner og kan suppleres:

  1. Gem navnet på en brugers tidszone som en brugerpræference (f.eks. America/Los_Angeles , ikke -0700 ).
  2. Få brugerhændelser/tidsdata indsendt lokalt til deres referenceramme (sandsynligvis en offset fra UTC, såsom -0700 ).
  3. Konverter tiden til UTC i applikationen og gemt ved hjælp af et TIMESTAMP WITH TIME ZONE kolonne.
  4. Returtidsanmodninger lokalt til en brugers tidszone (dvs. konverter fra UTC til America/Los_Angeles ).
  5. Indstil din databases timezone til UTC .

Denne mulighed virker ikke altid, fordi det kan være svært at få en brugers tidszone og derfor rådene om at bruge TIMESTAMP WITH TIME ZONE til lette applikationer. Når det er sagt, så lad mig forklare nogle baggrundsaspekter af denne mulighed 4 mere detaljeret.

Ligesom Mulighed 3, årsagen til WITH TIME ZONE er fordi tidspunktet, hvor noget skete, er absolut øjeblik i tiden. WITHOUT TIME ZONE giver en slægtning tidszone. Bland aldrig, nogensinde, aldrig absolutte og relative TIMESTAMPs.

Fra et programmatisk og konsistent perspektiv skal du sikre, at alle beregninger udføres ved hjælp af UTC som tidszone. Dette er ikke et PostgreSQL-krav, men det hjælper, når du integrerer med andre programmeringssprog eller miljøer. Indstilling af en CHECK på kolonnen for at sikre, at skrivningen til tidsstempelkolonnen har en tidszoneforskydning på 0 er en defensiv position, der forhindrer nogle få klasser af fejl (f.eks. et script dumper data til en fil, og noget andet sorterer tidsdataene ved hjælp af en leksikalsk sortering). Igen, PostgreSQL har ikke brug for dette for at udføre datoberegninger korrekt eller for at konvertere mellem tidszoner (dvs. PostgreSQL er meget dygtig til at konvertere tider mellem to vilkårlige tidszoner). For at sikre, at data, der går ind i databasen, gemmes med en offset på nul:

CREATE TABLE my_tbl (
  my_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  CHECK(EXTRACT(TIMEZONE FROM my_timestamp) = '0')
);
test=> SET timezone = 'America/Los_Angeles';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
ERROR:  new row for relation "my_tbl" violates check constraint "my_tbl_my_timestamp_check"
test=> SET timezone = 'UTC';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
INSERT 0 1

Det er ikke 100 % perfekt, men det giver en stærk nok anti-fodskydning, der sikrer, at dataene allerede er konverteret til UTC. Der er mange meninger om, hvordan man gør dette, men dette ser ud til at være det bedste i praksis ud fra min erfaring.

Kritik af databasens tidszonehåndtering er stort set berettiget (der er masser af databaser, der håndterer dette med stor inkompetence), men PostgreSQL’s håndtering af tidsstempler og tidszoner er ret fantastisk (på trods af et par "funktioner" hist og her). For eksempel en sådan funktion:

-- Make sure we're all working off of the same local time zone
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT NOW();
              now              
-------------------------------
 2011-05-27 15:47:58.138995-07
(1 row)

test=> SELECT NOW() AT TIME ZONE 'UTC';
          timezone          
----------------------------
 2011-05-27 22:48:02.235541
(1 row)

Bemærk at AT TIME ZONE 'UTC' fjerner tidszoneoplysninger og opretter et relativt TIMESTAMP WITHOUT TIME ZONE ved at bruge dit måls referenceramme (UTC ).

Ved konvertering fra en ufuldstændig TIMESTAMP WITHOUT TIME ZONE til et TIMESTAMP WITH TIME ZONE , den manglende tidszone arves fra din forbindelse:

test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
 date_part 
-----------
        -7
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
 date_part 
-----------
        -7
(1 row)

-- Now change to UTC    
test=> SET timezone = 'UTC';
SET
-- Create an absolute time with timezone offset:
test=> SELECT NOW();
              now              
-------------------------------
 2011-05-27 22:48:40.540119+00
(1 row)

-- Creates a relative time in a given frame of reference (i.e. no offset)
test=> SELECT NOW() AT TIME ZONE 'UTC';
          timezone          
----------------------------
 2011-05-27 22:48:49.444446
(1 row)

test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
 date_part 
-----------
         0
(1 row)

test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
 date_part 
-----------
         0
(1 row)

Den nederste linje:

  • gem en brugers tidszone som en navngivet etiket (f.eks. America/Los_Angeles ) og ikke en offset fra UTC (f.eks. -0700 )
  • brug UTC til alt, medmindre der er en tvingende grund til at gemme en offset, der ikke er nul
  • behandl alle UTC-tider, der ikke er nul, som en inputfejl
  • Bland og match aldrig relative og absolutte tidsstempler
  • brug også UTC som timezone i databasen, hvis det er muligt

Bemærkning til tilfældig programmeringssprog:Pythons datetime datatypen er meget god til at opretholde skelnen mellem absolutte vs relative tider (omend frustrerende i starten, indtil du supplerer det med et bibliotek som PyTZ).

REDIGER

Lad mig forklare forskellen mellem relativ vs absolut lidt mere.

Absolut tid bruges til at optage en begivenhed. Eksempler:"Bruger 123 er logget ind" eller "en gradueringsceremonie starter 2011-05-28 kl. 14.00 PST." Uanset din lokale tidszone, hvis du kunne teleportere til det sted, hvor begivenheden fandt sted, kunne du se begivenheden ske. De fleste tidsdata i en database er absolutte (og bør derfor være TIMESTAMP WITH TIME ZONE , ideelt set med en +0 offset og en tekstetiket, der repræsenterer reglerne, der styrer den bestemte tidszone - ikke en offset).

En relativ begivenhed ville være at optage eller planlægge tidspunktet for noget fra perspektivet af en endnu ikke-bestemt tidszone. Eksempler:"vores virksomheds døre åbner kl. 8.00 og lukker kl. 21.00", "lad os mødes hver mandag kl. 7.00 til et ugentligt morgenmadsmøde" eller "hver Halloween kl. 20.00." Generelt bruges relativ tid i en skabelon eller fabrik til begivenheder, og absolut tid bruges til næsten alt andet. Der er en sjælden undtagelse, der er værd at påpege, som burde illustrere værdien af ​​relative tider. For fremtidige begivenheder, der ligger langt nok ude i fremtiden, hvor der kan være usikkerhed om det absolutte tidspunkt, hvor noget kan forekomme, skal du bruge et relativt tidsstempel. Her er et eksempel fra den virkelige verden:

Antag, at det er år 2004, og du skal planlægge en levering den 31. oktober i 2008 kl. 13.00 på den amerikanske vestkyst (dvs. America/Los_Angeles /PST8PDT ). Hvis du har gemt det med absolut tid ved hjælp af ’2008-10-31 21:00:00.000000+00’::TIMESTAMP WITH TIME ZONE , ville leveringen have vist sig kl. 14.00, fordi den amerikanske regering vedtog Energy Policy Act af 2005, der ændrede reglerne for sommertid. I 2004, da leveringen var planlagt, var datoen 10-31-2008 ville have været Pacific Standard Time (+8000 ), men fra og med år 2005+ erkendte tidszonedatabaser, at 10-31-2008 ville have været Stillehavet sommertid (+0700 ). Lagring af et relativt tidsstempel med tidszonen ville have resulteret i en korrekt leveringsplan, fordi et relativt tidsstempel er immun over for Kongressens dårligt informerede manipulation. Hvor grænsen mellem at bruge relative vs absolutte tider til at planlægge ting er, er en uklar linje, men min tommelfingerregel er, at planlægning for alt i fremtiden længere end 3-6 måneder bør gøre brug af relative tidsstempler (planlagt =absolut vs planlagt =relativ ???).

Den anden/sidste type relativ tid er INTERVAL . Eksempel:"sessionen vil timeout 20 minutter efter, at en bruger logger på". En INTERVAL kan bruges korrekt med enten absolutte tidsstempler (TIMESTAMP WITH TIME ZONE ) eller relative tidsstempler (TIMESTAMP WITHOUT TIME ZONE ). Det er lige så korrekt at sige, "en brugersession udløber 20 minutter efter et vellykket login (login_utc + session_duration)" eller "vores morgenmorgenmøde kan kun vare 60 minutter (gentagende_starttid + møde_længde)".

Sidste forvirring:DATE , TIME , TIME WITHOUT TIME ZONE og TIME WITH TIME ZONE er alle relative datatyper. For eksempel:'2011-05-28'::DATE repræsenterer en relativ dato, da du ikke har nogen tidszoneoplysninger, som kan bruges til at identificere midnat. Tilsvarende '23:23:59'::TIME er relativ, fordi du hverken kender tidszonen eller DATE repræsenteret af tiden. Selv med '23:59:59-07'::TIME WITH TIME ZONE , du ved ikke hvad DATE er ville være. Og til sidst, DATE med en tidszone er faktisk ikke en DATE , det er et TIMESTAMP WITH TIME ZONE :

test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
      timezone       
---------------------
 2011-05-11 07:00:00
(1 row)

test=> SET timezone = 'UTC';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
      timezone       
---------------------
 2011-05-11 00:00:00
(1 row)

Det er en god ting at sætte datoer og tidszoner i databaser, men det er let at få subtilt forkerte resultater. Der kræves minimal yderligere indsats for at gemme tidsoplysninger korrekt og fuldstændigt, men det betyder ikke, at den ekstra indsats altid er påkrævet.



  1. Sådan fungerer EXP() i MariaDB

  2. Understøtter Postgres indlejrede eller autonome transaktioner?

  3. 40 spørgsmål du skal vide om R12.2

  4. Brug af ODBC med Salesforce og Okta Single Sign On (SSO)