Parece que desea almacenar una hora local con respecto a una determinada zona horaria. En ese caso, almacene una timestamp
(sin zona horaria) y la timezone
en una columna separada.
Por ejemplo, suponga que desea registrar un evento que ocurrirá a las 10 a. m. del 26 de febrero de 2030 en Chicago y debe ser a las 10 a. m. hora local independientemente de la regla de zona horaria vigente en esa fecha.
Si la base de datos almacena la marca de tiempo sin zona horaria:
unutbu=# select '2030-02-26 10:00:00'::timestamp as localtime, 'America/Chicago' AS tzone;
+---------------------+-----------------+
| localtime | tzone |
+---------------------+-----------------+
| 2030-02-26 10:00:00 | America/Chicago |
+---------------------+-----------------+
Luego, más tarde, puede encontrar la fecha y hora UTC del evento usando
unutbu=# select '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 16:00:00 |
+---------------------+
La consulta devuelve la fecha y hora UTC, 2030-02-26 16:00:00
, que corresponde a 2030-02-26 10:00:00
hora local en Chicago.
Usando AT TIME ZONE
retrasa la aplicación de las reglas de zona horaria a cuando se realiza la consulta en lugar de cuando timestamptz
fue insertado.
Usando AT TIME ZONE
en una timestamp
localiza la fecha y hora en la zona horaria dada, pero informa la fecha y hora en la zona horaria del usuario .Usando AT TIME ZONE
en un timestamptz
convierte la fecha y hora a la zona horaria dada, luego elimina el desplazamiento, devolviendo así una timestamp
.Arriba, AT TIME ZONE
se usa dos veces:primero para localizar una timestamp
y al lado para convertir el timestamptz
devuelto a una nueva zona horaria (UTC). El resultado es una timestamp
en UTC.
Aquí hay un ejemplo que demuestra AT TIME ZONE
comportamiento de en timestamp
s:
unutbu=# SET timezone = 'America/Chicago';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 10:00:00-06 |
+------------------------+
unutbu=# SET timezone = 'America/Los_Angeles';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 08:00:00-08 |
+------------------------+
2030-02-26 10:00:00-06
y 2030-02-26 08:00:00-08
son las mismas fechas pero notificados en diferentes zonas horarias de usuario. Esto muestra que las 10 a. m. en Chicago son las 8 a. m. en Los Ángeles (usando las definiciones de zonas horarias actuales):
unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Una alternativa al uso de AT TIME ZONE
dos veces es para establecer la zona horaria del usuario
a UTC
. Entonces podrías usar
select localtime AT TIME ZONE tzone
Tenga en cuenta que cuando se hace de esta manera, un timestamptz
se devuelve en lugar de una timestamp
.
Tenga en cuenta que almacenar las horas locales puede ser problemático porque puede haber horas inexistentes y horas ambiguas. Por ejemplo, 2018-03-11 02:30:00
es una hora local inexistente en America/Chicago
. Postgresql normaliza las horas locales inexistentes asumiendo que se refiere a la hora correspondiente después de que haya comenzado el horario de verano (DST) (como si alguien hubiera olvidado adelantar su reloj):
unutbu=# select '2018-03-11 02:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
unutbu=# select '2018-03-11 03:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
Un ejemplo de una hora local ambigua es 2018-11-04 01:00:00
en America/Chicago
. Ocurre dos veces debido al horario de verano. Postgresql resuelve esta ambigüedad eligiendo la hora posterior, después de que finalice el horario de verano:
unutbu=# select '2018-11-04 01:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 07:00:00 |
+---------------------+
Tenga en cuenta que esto significa que no hay forma de referirse a 2018-11-04 06:00:00 UTC
almacenando las horas locales en America/Chicago
zona horaria:
unutbu=# select '2018-11-04 00:59:59'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 05:59:59 |
+---------------------+