Zonas horarias
Cómo ClickHouse convierte cadenas de DateTime
DateTime:
- Si una columna se define con una zona horaria (
DateTime64(9, ‘Asia/Tokyo’)), el valor de cadena se tratará como una marca de tiempo en esa zona horaria.2026-01-01 13:00:00será2026-01-01 04:00:00en horaUTC. - Si una columna no tiene definida ninguna zona horaria, solo se usa la zona horaria del servidor. Importante: la configuración
session_timezoneno tiene ningún efecto. Por lo tanto, si la zona horaria del servidor esUTCy la zona horaria de la sesión esAmerica/Los_Angeles,2026-01-01 13:00:00se escribirá como horaUTC. - Cuando se lee un valor de una columna sin una zona horaria definida, se usa
session_timezoneo, si no está configurado, la zona horaria del servidor. Por eso, la lectura de marcas de tiempo como cadenas puede verse afectada porsession_timezone. No hay nada incorrecto en ello, pero conviene tenerlo en cuenta.
Escritura de timestamps en distintas zonas horarias
us-west con la zona horaria local UTC-8, y que necesitamos escribir un timestamp local 2026-01-01 02:00:00 que en UTC es 2026-01-01 10:00:00:
- Escribirlo como una cadena requiere convertirlo a la zona horaria del servidor o de la columna.
- Escribirlo como una estructura de tiempo nativa del lenguaje requiere que el driver conozca la zona horaria de destino, pero:
- No siempre es posible
- La API del driver no está bien diseñada para esto
- La única manera es describir qué transformaciones se realizarán para que la aplicación pueda compensarlo (o escribir un Unix timestamp como un número)
Java y las API de timestamp de JDBC
- Usa la clase
Timestamp, que en realidad es un Unix timestamp.- Cuando se usa con un objeto
Calendar, permite reinterpretar elTimestampen la zona horaria del calendario. Timestamptiene un calendario interno que no es muy evidente.
- Cuando se usa con un objeto
- Usa la clase
LocalDateTime, que se puede convertir fácilmente a cualquier zona horaria, pero no existe ningún método que permita pasar una zona horaria de destino. - Usa la clase
ZonedDateTime, que ayuda con la conversión de zona horaria al escribir en unDateTimesin zona horaria (porque sabemos que debe usarse la zona horaria del servidor).- Pero escribir un
ZonedDateTimeen una columna con una zona horaria definida requiere que el usuario compense la conversión del driver.
- Pero escribir un
- Usa
Longpara escribir milisegundos de Unix timestamp. - Usa
Stringpara realizar todas las conversiones del lado de la aplicación (lo cual no es muy portable).
Fecha
Date y Date32 para almacenar fechas. Ambos tipos usan un número de días desde la época (1970-01-01). Date usa únicamente números positivos de días, por lo que su rango termina en 2149-06-06. Date32 admite números negativos de días para cubrir fechas anteriores a 1970-01-01, pero su rango es más reducido (de 1900-01-01 a 2100-01-01, donde 0 es 1970-01-01). ClickHouse considera 2026-01-01 como 2026-01-01 en cualquier zona horaria, y no existe ningún parámetro de zona horaria para las definiciones de columnas.
Uso de java.time.LocalDate
java.time.LocalDate. El cliente utiliza esta clase para almacenar el valor de las columnas Date y Date32 (leyendo LocalDate.ofEpochDay((long)readUnsignedShortLE())).
Recomendamos usar java.time.LocalDate porque no se ve afectada por las conversiones de zona horaria y forma parte de la API moderna de fecha y hora.
Uso de java.sql.Date
LocalDate se introdujo en Java 8. Antes de eso, se usaba java.sql.Date para escribir y leer fechas. Internamente, esta clase es un contenedor de un instante (un valor temporal que representa un punto absoluto en el tiempo). Debido a ello, toString() devuelve una fecha distinta según la zona horaria en la que se ejecute la JVM. Esto obliga al driver a construir los valores con cuidado y requiere que el usuario sea consciente de ello.
Reinterpretación basada en calendario
java.sql.ResultSet tiene un método para obtener valores de fecha que acepta un Calendar, y java.sql.PreparedStatement incluye un método similar. Esto se diseñó para permitir que el driver JDBC reinterprete un valor de fecha en la zona horaria especificada. Por ejemplo, la DB tiene el valor 2026-01-01, pero la aplicación quiere interpretar esta fecha como la medianoche en Tokyo. Esto significa que el objeto java.sql.Date devuelto tendrá un instante específico y, al convertirlo a la zona horaria local, puede corresponder a una fecha distinta debido a la diferencia horaria. Podemos lograr lo mismo con LocalDate mediante java.time.LocalDate#atStartOfDay(java.time.ZoneId).
El driver JDBC de ClickHouse siempre devuelve un objeto java.sql.Date que apunta a la fecha local a medianoche. En otras palabras, si la fecha es 2026-01-01, nos referimos a 2026-01-01 12:00 AM en la zona horaria de la JVM (el mismo comportamiento que los drivers JDBC de PostgreSQL y MariaDB).
Time
’6:30’ es el mismo en cualquier lugar donde se lea.
Tipos de tiempo de ClickHouse
Time y Time64 se introdujeron en la versión 25.6. Antes de eso, se usaban en su lugar los tipos de timestamp DateTime y DateTime64 (que se analizan más adelante en esta guía). Time se almacena como un entero de 32 bits que representa una cantidad de segundos, y su rango es [-999:59:59, 999:59:59]. Time64 se codifica como un Decimal64 sin signo y almacena distintas unidades de tiempo según la precisión. Las opciones habituales son 3 (milisegundos), 6 (microsegundos) y 9 (nanosegundos). El rango de valores de precisión es [0, 9].
Correspondencia de tipos de Java
Time y Time64 y los almacena como LocalDateTime. Esto se hace para admitir el rango de tiempos negativos (LocalTime no lo admite). En este caso, la parte de la fecha corresponde a la fecha de época 1970-01-01, por lo que los valores negativos quedarán antes de esa fecha.
La compatibilidad principal con los tipos de tiempo se implementa mediante LocalTime (cuando el valor cabe dentro de un día) y Duration para abarcar todo el rango de valores. LocalDateTime solo puede usarse para lectura.
Uso de java.sql.Time
java.sql.Time está limitado al intervalo de LocalTime. Internamente, java.sql.Time se convierte en un literal de cadena. El valor puede modificarse usando un parámetro Calendar con PreparedStatement#setTime().
La función toTime
toTimesiempre requiereDate,DateTimeu otro tipo similar. No acepta cadenas. Problema relacionado: https://github.com/ClickHouse/ClickHouse/issues/89896- Es un alias de
toTimeWithFixedDate. - Hay un problema relacionado con la zona horaria: https://github.com/ClickHouse/ClickHouse/pull/90310
timestamp
1970-01-01 00:00:00 UTC (un número negativo de segundos representa una timestamp anterior a la hora Unix, y un número positivo representa una posterior). Esta representación es fácil de calcular y manejar si el observador se encuentra en la zona horaria UTC o la usa en lugar de su zona horaria local.
Tipos de timestamp de ClickHouse
DateTime (entero de 32 bits; la resolución siempre es en segundos) y DateTime64 (entero de 64 bits; la resolución depende de la definición). Los valores siempre se almacenan como timestamps UTC. Esto significa que, cuando se representan como números, no se aplica ninguna conversión de zona horaria.
Representación en cadena y comportamiento de la zona horaria
- Si no se especifica ninguna zona horaria en la definición de la columna y se pasa una cadena al escribir, se convertirá de la zona horaria del servidor a un número de timestamp UTC. Cuando se lee un valor de esa columna, se convertirá de un timestamp UTC a un timestamp literal usando la zona horaria del servidor o de la sesión (se aplica un enfoque similar a los literales de timestamp en expresiones donde la zona horaria no se define explícitamente).
- Si se especifica una zona horaria en la definición de la columna, solo se usa esa zona horaria en todas las conversiones de cadenas. Esto contradice la lógica aplicada cuando no se especifica ninguna zona horaria, por lo que requiere comprender bien cómo se escriben los datos para cada columna de la consulta.
- Si se pasa una fecha como cadena en un formato que incluye una zona horaria, se necesita una función de conversión. Normalmente se usa
parseDateTimeBestEffort.
Cómo el driver JDBC gestiona los timestamps
DateTime y DateTime64 se leen y almacenan en el cliente como java.time.ZonedDateTime, lo que facilita la conversión de estos valores a cualquier otra zona horaria (se conserva la información de la zona horaria).
Error habitual con toDateTime64
toDateTime64 usa la zona horaria del servidor y no tiene en cuenta la zona horaria de origen.
Tablas de conversión
Date no pueden leerse como java.sql.Timestamp porque no tienen parte de hora.
El driver no convierte valores enteros en ningún valor de fecha u hora. Llamar a pstmt.setLong("timestamp", 1772132359L) hará que 1772132359 se escriba como un número en el servidor, que se tratará como
un Unix timestamp UTC en segundos.
Escritura de valores con PreparedStatement#setObject
PreparedStatement#setObject(column, value):
El tipo de la columna debe considerarse desconocido. La aplicación es la que debe decidir qué pasar a la sentencia preparada.
Lectura de valores con ResultSet#getObject
ResultSet#getObject(column, class):
Uso de métodos basados en calendario
ResultSet#getTime(column, calendar) y ResultSet#getDate(column, calendar) si los valores se almacenaron con PreparedStatement#setTime(param, value, calendar) y PreparedStatement#setDate(param, value, calendar), respectivamente.