Respuestas:
Las diferencias están cubiertas en la documentación de PostgreSQL para los tipos de fecha / hora . Sí, el tratamiento de TIME
o TIMESTAMP
difiere entre uno WITH TIME ZONE
o WITHOUT TIME ZONE
. No afecta cómo se almacenan los valores; afecta cómo se interpretan.
Los efectos de las zonas horarias en estos tipos de datos se tratan específicamente en los documentos. La diferencia surge de lo que el sistema puede saber razonablemente sobre el valor:
Con una zona horaria como parte del valor, el valor se puede representar como una hora local en el cliente.
Sin una zona horaria como parte del valor, la zona horaria predeterminada obvia es UTC, por lo que se representa para esa zona horaria.
El comportamiento difiere dependiendo de al menos tres factores:
WITH TIME ZONE
o WITHOUT TIME ZONE
) del valor.Aquí hay ejemplos que cubren las combinaciones de esos factores:
foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+09
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 06:00:00+09
(1 row)
foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+11
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 08:00:00+11
(1 row)
timestamp with time zone
y timestamp without time zone
, en Postgres *, en realidad no almacenan información de zona horaria. Puede confirmar esto con un vistazo a la página de documentos de tipo de datos: ambos tipos ocupan el mismo número de octetos y tienen el rango de valores guardados, por lo que no hay espacio para almacenar información de zona horaria. El texto de la página confirma esto. Algo inapropiado: "sin tz" significa "ignorar el desplazamiento al insertar datos" y "con tz" significa "utilizar el desplazamiento para ajustar a UTC".
Intento explicarlo de manera más comprensible que la documentación de PostgreSQL referida.
Ninguna de las TIMESTAMP
variantes almacena una zona horaria (o un desplazamiento), a pesar de lo que sugieren los nombres. La diferencia está en la interpretación de los datos almacenados (y en la aplicación prevista), no en el formato de almacenamiento en sí:
TIMESTAMP WITHOUT TIME ZONE
almacena la fecha y hora local (también conocida como fecha del calendario de pared y hora del reloj de pared). Su zona horaria no está especificada por lo que PostgreSQL puede decir (aunque su aplicación puede saber qué es). Por lo tanto, PostgreSQL no realiza una conversión relacionada con la zona horaria en la entrada o salida. Si el valor se ingresó en la base de datos como '2011-07-01 06:30:30'
, entonces no importa en qué zona horaria lo muestre más tarde, seguirá diciendo año 2011, mes 07, día 01, 06 horas, 30 minutos y 30 segundos (en algún formato). Además, cualquier desplazamiento o la zona horaria que especifique en la entrada es ignorada por PostgreSQL, por lo que '2011-07-01 06:30:30+00'
y '2011-07-01 06:30:30+05'
son los mismos que acaba '2011-07-01 06:30:30'
. Para desarrolladores de Java: es análogo a java.time.LocalDateTime
.
TIMESTAMP WITH TIME ZONE
almacena un punto en la línea de tiempo UTC. El aspecto (cuántas horas, minutos, etc.) depende de su zona horaria, pero siempre se refiere al mismo instante "físico" (como el momento de un evento físico real). La entrada se convierte internamente a UTC, y así es como se almacena. Para eso, se debe conocer el desplazamiento de la entrada, por lo que cuando la entrada no contiene un desplazamiento explícito o zona horaria (como '2011-07-01 06:30:30'
) se supone que está en la zona horaria actual de la sesión de PostgreSQL; de lo contrario, se utiliza la compensación o zona horaria especificada explícitamente (como en '2011-07-01 06:30:30+05'
). La salida se muestra convertida a la zona horaria actual de la sesión de PostgreSQL. Para desarrolladores de Java: es análogo java.time.Instant
(aunque con una resolución más baja), pero con JDBC y JPA 2.2 se supone que debes mapearlo java.time.OffsetDateTime
( java.util.Date
ojava.sql.Timestamp
por supuesto).
Algunos dicen que ambas TIMESTAMP
variaciones almacenan la fecha y hora UTC. Más o menos, pero es confuso decirlo así en mi opinión. TIMESTAMP WITHOUT TIME ZONE
se almacena como un TIMESTAMP WITH TIME ZONE
, que se representa con la zona horaria UTC y da el mismo año, mes, día, horas, minutos, segundos y microsegundos que en la fecha y hora local. Pero no está destinado a representar el punto en la línea de tiempo que dice la interpretación UTC, es solo la forma en que se codifican los campos de fecha y hora locales. (Es un grupo de puntos en la línea de tiempo, ya que la zona de tiempo real no es UTC; no sabemos qué es).
TIMESTAMP WITH TIME ZONE
como a Instant
. Ambos representan un punto en la línea de tiempo en UTC. Instant
es preferible, en mi opinión, OffsetDateTime
ya que es más autodocumentado: A TIMESTAMP WITH TIME ZONE
siempre se recupera de la base de datos como UTC, y un Instant
siempre está en UTC, por lo que es una coincidencia natural, mientras que uno OffsetDateTime
puede llevar otras compensaciones.
OffsetDateTime
como el tipo de Java mapeado. No estoy seguro de si Instance
todavía se admite de forma no oficial en alguna parte.
'2011-07-01 06:30:30+00'
y '2011-07-01 06:30:30+05'
se ignora, pero soy capaz de hacerlo insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);
y lo convertirá a utc correctamente. donde fecha es marca de tiempo sin zona horaria. Estoy tratando de entender cuál es el valor principal de la marca de tiempo con la zona horaria y tengo problemas.
::timestamptz
. Con eso, convierte la cadena a TIMESTAMP WITH TIME ZONE
, y cuando eso se convierta aún más WITHOUT TIME ZONE
, almacenará el día del "calendario de pared" y la hora del reloj de pared de ese instante como se ve desde su zona horaria de sesión (que tal vez sea UTC). Todavía solo será una marca de tiempo local con desplazamiento no especificado (sin zona).
Aquí hay un ejemplo que debería ayudar. Si tiene una marca de tiempo con una zona horaria, puede convertir esa marca de tiempo en cualquier otra zona horaria. Si no tiene una zona horaria base, no se convertirá correctamente.
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
Salida:
-[ RECORD 1 ]---------------------------
now | 2018-09-15 17:01:36.399357+03
now | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
timestamp
y qué timestamptz
significa. timestamptz
significa un punto absoluto en el tiempo (UTC) mientras que timestamp
denota lo que mostró el reloj en una zona horaria determinada. Por lo tanto, cuando se convierte timestamptz
a una zona horaria, se pregunta qué mostró el reloj en Nueva York en este momento absoluto. mientras que al "convertir" a timestamp
, se pregunta cuál fue el punto absoluto en el tiempo cuando el reloj de Nueva York mostró x.
AT TIME ZONE
constructo es un desafío para la mente propio, incluso si ya comprende los tipos WITH
vs. WITHOUT TIME ZONE
Entonces es una elección curiosa para explicarlos. (: ( AT TIME ZONE
convierte una WITH TIME ZONE
marca de tiempo en una WITHOUT TIME ZONE
marca de tiempo, y viceversa ... no es exactamente obvio.)
now()::timestamp AT TIME ZONE 'CST'
no tiene sentido, a menos que en qué instante un reloj para la zona 'CST' muestre la hora en que se muestra actualmente su reloj local