El tipo de datos timestamp
es el nombre corto de timestamp without time zone
.
La otra opción timestamptz
es la abreviatura de timestamp with time zone
.
timestamptz
es el tipo preferido en la familia de fecha / hora, literalmente. Se ha typispreferred
establecido pg_type
, que puede ser relevante:
Almacenamiento interno y época
Internamente, las marcas de tiempo ocupan 8 bytes de almacenamiento en el disco y en la RAM. Es un valor entero que representa el recuento de microsegundos de la época de Postgres, 2000-01-01 00:00:00 UTC.
Postgres también tiene un conocimiento incorporado del tiempo de recuento de tiempo UNIX comúnmente utilizado desde la época UNIX, 1970-01-01 00:00:00 UTC, y lo usa en funciones to_timestamp(double precision)
o EXTRACT(EPOCH FROM timestamptz)
.
El código fuente:
* Las marcas de tiempo, así como los campos de intervalos h / m / s, se almacenan como
* valores int64 con unidades de microsegundos. (Érase una vez
* valores dobles con unidades de segundos.)
Y:
/ * Equivalentes de fecha juliana del día 0 en el cálculo de Unix y Postgres * /
#define UNIX_EPOCH_JDATE 2440588 / * == date2j (1970, 1, 1) * /
#define POSTGRES_EPOCH_JDATE 2451545 / * == date2j (2000, 1, 1) * /
La resolución de microsegundos se traduce en un máximo de 6 dígitos fraccionarios por segundos.
timestamp
Un valor escrito como indica a Postgres que no se proporciona explícitamente ninguna zona horaria. Se asume la zona horaria actual. ¡Postgres ignora cualquier modificador de zona horaria agregado por error!timestamp
[without time zone]
No se cambian las horas para mostrar. Con la misma configuración de zona horaria, todo está bien. Para una configuración de zona horaria diferente, el significado cambia, pero el valor y la visualización permanecen igual.
timestamptz
El manejo de timestamp with time zone
es sutilmente diferente. Cito el manual aquí :
Para timestamp with time zone
, el valor almacenado internamente siempre está en UTC (Tiempo Universal Coordinado ...)
El énfasis en negrita es mío. La zona horaria en sí misma nunca se almacena . Es un modificador de entrada que se usa para calcular la marca de tiempo UTC correspondiente, que se almacena, o un modificador de salida que se usa para calcular la hora local a mostrar, con el desplazamiento de zona horaria adjunto. Si no agrega un desplazamiento para la timestamptz
entrada, se supone la configuración de zona horaria actual de la sesión. Todos los cálculos se realizan con valores de marca de tiempo UTC. Si tiene que (o puede que tenga que) lidiar con más de una zona horaria, use timestamptz
.
A los clientes como psql o pgAdmin o cualquier aplicación que se comunique a través de libpq (como Ruby con la gema pg) se les presenta la marca de tiempo más el desplazamiento para la zona horaria actual o de acuerdo con una zona horaria solicitada (ver más abajo). Siempre es el mismo punto en el tiempo , solo varía el formato de visualización. O, como dice el manual :
Todas las fechas y horas con reconocimiento de zona horaria se almacenan internamente en UTC. Se convierten a la hora local en la zona especificada por el
parámetro de configuración TimeZone antes de mostrarse al cliente.
Considere este ejemplo simple (en psql):
db = # SELECCIONAR marca de tiempo '2012-03-05 20:00 +03 ';
marca de tiempo
------------------------
2012-03-05 18:00:00 +01
El énfasis en negrita es mío. ¿Que pasó aquí?
Elegí un desplazamiento de zona horaria arbitrario +3
para el literal de entrada. Para Postgres, esta es solo una de las muchas formas de ingresar la marca de tiempo UTC2012-03-05 17:00:00
. El resultado de la consulta se muestra para la zona horaria actual que configura Viena / Austria en mi prueba, que tiene un desplazamiento +1
durante el invierno y +2
durante el verano: 2012-03-05 18:00:00+01
porque cae en invierno.
Postgres ya ha olvidado cómo se ha introducido este valor. Todo lo que recuerda es el valor y el tipo de datos. Al igual que con un número decimal. numeric '003.4'
, numeric '3.40'
o numeric '+3.4'
- todos dan como resultado exactamente el mismo valor interno.
AT TIME ZONE
Tan pronto como comprenda esta lógica, puede hacer lo que quiera. Todo lo que falta ahora es una herramienta para interpretar o representar literales de marca de tiempo de acuerdo con una zona horaria específica. Ahí es donde AT TIME ZONE
entra en juego la construcción. Hay dos casos de uso diferentes. timestamptz
se convierte timestamp
y viceversa.
Para ingresar al UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
... que es equivalente a:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
Para mostrar el mismo punto en el tiempo que EST timestamp
(hora estándar del este):
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
Así es, AT TIME ZONE 'UTC'
dos veces . El primero interpreta eltimestamp
valor como (dado) la marca de tiempo UTC que devuelve el tipo timestamptz
. El segundo convierte el timestamptz
a timestamp
en la zona horaria dada 'EST', lo que muestra un reloj en la zona horaria EST en este punto único en el tiempo.
Ejemplos
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
Devuelve 8 (o 9) filas idénticas con columnas de marca de tiempo que contienen la misma marca de tiempo UTC 2012-03-05 17:00:00
. La novena fila funciona en mi zona horaria, pero es una trampa malvada. Vea abajo.
① Las filas 6 - 8 con el nombre de la zona horaria y la abreviatura de la zona horaria para la hora de Hawái están sujetas a DST (horario de verano) y pueden diferir, aunque no actualmente. Un nombre de zona horaria como 'US/Hawaii'
conoce las reglas de DST y todos los cambios históricos automáticamente, mientras que una abreviatura como HST
es solo un código tonto para un desplazamiento fijo. Es posible que deba agregar una abreviatura diferente para el horario de verano / estándar. El nombre interpreta correctamente cualquier marca de tiempo en la zona horaria dada. Una abreviatura es barata, pero debe ser la correcta para la marca de tiempo dada:
El horario de verano no se encuentra entre las ideas más brillantes que la humanidad haya tenido.
② La fila 9, marcada como pistola cargada, funciona para mí , pero solo por coincidencia. Si expulsa explícitamente un literal a timestamp [without time zone]
, ¡ se ignora cualquier desplazamiento de zona horaria ! Solo se utiliza la marca de tiempo desnuda. El valor se coacciona automáticamente timestamptz
en el ejemplo para que coincida con el tipo de columna. Para este paso, timezone
se supone la configuración de la sesión actual, que resulta ser la misma zona horaria +1
en mi caso (Europa / Viena). Pero probablemente no en su caso, lo que dará como resultado un valor diferente. En resumen: no emita timestamptz
literales timestamp
o perderá el desplazamiento de la zona horaria.
Tus preguntas
El usuario almacena un horario, digamos el 17 de marzo de 2012 a las 7 p.m. No quiero que se almacenen las conversiones de zona horaria o la zona horaria.
La zona horaria en sí misma nunca se almacena. Use uno de los métodos anteriores para ingresar una marca de tiempo UTC.
Solo uso la zona horaria especificada por los usuarios para obtener registros 'antes' o 'después' de la hora actual en la zona horaria local de los usuarios.
Puede usar una consulta para todos los clientes en diferentes zonas horarias.
Por tiempo global absoluto:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Por hora según el reloj local:
SELECT * FROM tbl WHERE time_col > now()::time
¿Todavía no estás cansado de la información de fondo? Hay más en el manual.
Si desea negociar en UTC por defecto:
En
config/application.rb
, agregue:Luego, si almacena el nombre de la zona horaria del usuario actual
current_user.timezone
, puede decirlo.current_user.timezone
debe ser un nombre válido de zona horaria, de lo contrario obtendráArgumentError: Invalid Timezone
, vea la lista completa .fuente