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

PostgreSQL konverterede forkert fra tidsstempel uden tidszone til tidsstempel med tidszone

Vigtige ting at forstå

timestamp without time zone AT TIME ZONE omfortolker et timestamp som at være i den tidszone med det formål at konvertere den til UTC .

timestamp with time zone AT TIME ZONE konverterer en timestamptz ind i et timestamp i den angivne tidszone.

PostgreSQL bruger ISO-8601-tidszoner, som angiver, at øst for Greenwich er positiv ... medmindre du bruger en POSIX-tidszonespecifikation, i hvilket tilfælde den følger POSIX. Der opstår sindssyge.

Hvorfor den første giver et uventet resultat

Tidsstempler og tidszoner i SQL er forfærdelige. Dette:

select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';

fortolker den ukendte bogstavelige '2011-12-30 00:30:00' som timestamp without time zone , som Pg antager er i den lokale tidszone, medmindre andet er fortalt. Når du bruger AT TIME ZONE , det er (ifølge specifikationen) genfortolket som et timestamp with time zone i tidszonen EST5EDT gemmes derefter som en absolut tid i UTC - så den konverteres fra EST5EDT til UTC, dvs. tidszoneforskydningen bliver trukket fra . x - (-5) er x + 5 .

Dette tidsstempel, justeret til UTC-lagring, justeres derefter for din server TimeZone indstilling for visning, så den bliver vist i lokal tid.

Hvis du i stedet ønsker at sige "Jeg har dette tidsstempel i UTC-tid og ønsker at se, hvad den ækvivalente lokale tid i EST5EDT er", hvis du vil være uafhængig af serverens TimeZone-indstilling, skal du skrive noget som:

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE 'EST5EDT';

Dette siger "Givet tidsstempel 2011-12-30 00:30:00, behandle det som et tidsstempel i UTC, når du konverterer til timestamptz, og konverter derefter det tidsstempel til en lokal tid i EST5EDT".

Forfærdeligt, ikke? Jeg vil gerne give en virksomhed tale med hvem der end har besluttet sig for den vanvittige semantik i AT TIME ZONE - det burde virkelig være noget i stil med timestamp CONVERT FROM TIME ZONE '-5' og timestamptz CONVERT TO TIME ZONE '+5' . Også timestamp with time zone skal faktisk bære sin tidszone med sig, ikke gemmes i UTC og automatisk konverteres til lokaltid.

Hvorfor den anden virker (så længe TimeZone =UTC)

Din originale "virker"-version:

select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';

vil kun være korrekt, hvis TimeZone er indstillet til UTC, fordi tekst-til-tidsstempel-castet antager TimeZone, når en ikke er angivet.

Hvorfor virker den tredje

To problemer ophæver hinanden.

Den anden version, der ser ud til at virke, er TimeZone-uafhængig, men den virker kun, fordi to problemer ophæver sig selv. For det første, som forklaret ovenfor, timestamp without time zone AT TIME ZONE omfortolker tidsstemplet som værende i denne tidszone for konvertering til et UTC-tidsstempel; dette fratrækker effektivt tidszoneforskydningen.

Men af ​​årsager, som jeg ikke kender, bruger PostgreSQL tidsstempler med det omvendte fortegn til det, jeg er vant til at se de fleste steder. Se dokumentationen:

Et andet problem at huske på er, at i POSIX-tidszonenavne bruges positive forskydninger for lokationer vest for Greenwich. Alle andre steder følger PostgreSQL ISO-8601-konventionen om, at positive tidszoneforskydninger er øst for Greenwich.

Det betyder, at EST5EDT er det samme som +5 , ikke -5 . Det er derfor, det virker:fordi du trækker tz-forskydningen, ikke tilføjer det, men du trækker en negeret offset!

Det du skal bruge for at få det korrekt er i stedet:

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE '+5';



  1. NEWID() vs NEWSEQUENTIALID() i SQL Server:Hvad er forskellen?

  2. Sådan opretter du sekvens i MySQL

  3. Hvad skal jeg vælge - JSON eller SQLite?

  4. PostgreSQL-ækvivalent for TOP n WITH TIES:LIMIT with ties?