sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL konverterade fel från tidsstämpel utan tidszon till tidsstämpel med tidszon

Nyckelsaker att förstå

timestamp without time zone AT TIME ZONE omtolkar en timestamp som att vara i den tidszonen i syfte att konvertera den till UTC .

timestamp with time zone AT TIME ZONE konverterar en timestamptz till en timestamp vid den angivna tidszonen.

PostgreSQL använder ISO-8601-tidszoner, som anger att öster om Greenwich är positiv ... om du inte använder en POSIX-tidszonsspecifikation, i vilket fall den följer POSIX. Vansinne uppstår.

Varför den första ger ett oväntat resultat

Tidsstämplar och tidszoner i SQL är hemska. Detta:

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

tolkar den okända bokstaven '2011-12-30 00:30:00' som timestamp without time zone , som Pg antar är i den lokala tidszonen om inget annat anges. När du använder AT TIME ZONE , den är (enligt specifikationen) omtolkad som en timestamp with time zone i tidszonen EST5EDT lagras sedan som en absolut tid i UTC - så den konverteras från EST5EDT till UTC, dvs tidszonsförskjutningen subtraheras . x - (-5) är x + 5 .

Denna tidsstämpel, justerad till UTC-lagring, justeras sedan för din server TimeZone inställning för visning så att den visas i lokal tid.

Om du istället vill säga "Jag har denna tidsstämpel i UTC-tid, och vill se vad motsvarande lokal tid i EST5EDT är", om du vill vara oberoende av serverns TimeZone-inställning måste du skriva något i stil med:

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

Detta säger "Med tanke på tidsstämpel 2011-12-30 00:30:00, behandla den som en tidsstämpel i UTC när du konverterar till timestamptz, konvertera sedan den tidsstämpeln till en lokal tid i EST5EDT".

Hemskt, eller hur? Jag vill ge ett företag som pratar med vem som än bestämde sig för den galna semantiken i AT TIME ZONE - det borde verkligen vara något i stil med timestamp CONVERT FROM TIME ZONE '-5' och timestamptz CONVERT TO TIME ZONE '+5' . Även timestamp with time zone bör faktiskt bära sin tidszon med sig, inte lagras i UTC och automatiskt konverteras till lokal tid.

Varför den andra fungerar (så länge som TimeZone =UTC)

Din ursprungliga "works"-version:

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

kommer bara att vara korrekt om TimeZone är inställt på UTC, eftersom text-till-tidsstamptz-casten antar TimeZone när en inte är specificerad.

Varför den tredje fungerar

Två problem tar bort varandra.

Den andra versionen som verkar fungera är TimeZone-oberoende, men den fungerar bara för att två problem avbryter sig själva. Först, som förklarat ovan, timestamp without time zone AT TIME ZONE omtolkar tidsstämpeln som varande i den tidszonen för konvertering till en UTC-tidsstämpeltz; detta subtraherar effektivt tidszonsförskjutningen.

Men av skäl som jag inte känner till använder PostgreSQL tidsstämplar med det omvända tecknet till vad jag är van vid att se de flesta platser. Se dokumentationen:

En annan fråga att tänka på är att i POSIX-tidszonsnamn används positiva förskjutningar för platser väster om Greenwich. Överallt annars följer PostgreSQL ISO-8601-konventionen att positiva tidszonförskjutningar är öster om Greenwich.

Detta betyder att EST5EDT är samma som +5 , inte -5 . Det är därför det fungerar:för att du subtraherar tz-offset och inte adderar den, men du subtraherar en negerad offset!

Vad du behöver för att få det korrekt är istället:

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



  1. CHAR() Exempel i MySQL

  2. SQL Server 2005 Hur skapar man en unik begränsning?

  3. MySQL beviljar privilegier till användare för databas

  4. SQL Server Parallell Backup Restore -2