Depende del tipo de cita.
Hay dos tipos de citas:
- Un momento, un punto específico en la línea de tiempo, ignorando cualquier cambio en las reglas de la zona horaria.
Ejemplo:lanzamiento de un cohete. - Una fecha y hora del día que deben ajustarse a los cambios en las reglas de la zona horaria.
Ejemplo:visita médica/dental.
Momento
Si estamos reservando el lanzamiento de un cohete, por ejemplo, no nos importa la fecha y la hora del día. Solo nos importa el momento en que (a) los cielos se alinean y (b) esperamos un clima favorable.
Si en el intervalo de tiempo los políticos cambian las reglas de la zona horaria en uso en nuestro sitio de lanzamiento o en nuestras oficinas, eso no tiene efecto en nuestra cita de lanzamiento. Si los políticos que gobiernan nuestro sitio de lanzamiento adoptan Daylight Saving Time (DST) , el momento de nuestro lanzamiento sigue siendo el mismo. Si los políticos que gobiernan nuestras oficinas deciden cambiar el reloj media hora antes debido a relaciones diplomáticas con un país vecino, el momento de nuestro lanzamiento sigue siendo el mismo.
Para tal cita, sí, su enfoque sería correcto. Registrarías la cita en UTC
usando una columna de tipo TIMESTAMP WITH TIME ZONE
. Tras la recuperación, ajuste a cualquier zona horaria que prefiera el usuario.
Bases de datos como Postgres
use cualquier información de zona horaria que acompañe a una entrada para ajustarse a UTC y luego elimine esa información de zona horaria. Cuando recupera el valor de Postgres, siempre representará una fecha con la hora del día como se ve en UTC. Tenga cuidado, algunas herramientas o middleware pueden tener la anticaracterística de aplicar alguna zona horaria predeterminada entre la recuperación de la base de datos y la entrega al programador. Pero sea claro:Postgres siempre guarda y recupera valores de tipo TIMESTAMP WITH TIME ZONE
en UTC, siempre UTC, y offset-from-UTC
de cero horas-minutos-segundos.
Aquí hay un ejemplo de código Java.
LocalDate launchDateAsSeenInRome = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime launchTimeAsSeenInRome = LocalTime.of( 21 , 0 ) ;
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
// Assemble those three parts to determine a moment.
ZonedDateTime launchMomentAsSeenInRome = ZonedDateTime.of( launchDateAsSeenInRome , launchTimeAsSeenInRome , zoneEuropeRome ) ;
Para ver el mismo momento en UTC, convierte a un Instant
. Un Instant
objeto siempre representa un momento como se ve en UTC.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ; // Adjust from Rome time zone to UTC.
El Z
al final del ejemplo de cadena anterior hay notación estándar para UTC y se pronuncia "Zulu".
Desafortunadamente, el equipo de JDBC 4.2 no solicitó soporte para Instant
o ZonedDateTime
tipos Así que su controlador JDBC
puede o no ser capaz de leer/escribir dichos objetos en su base de datos. Si no, simplemente convierta a OffsetDateTime
. Los tres tipos representan un momento, un punto específico en la línea de tiempo. Pero OffsetDateTime
tiene soporte requerido por JDBC
4.2
por razones que se me escapan.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;
Escribiendo en la base de datos.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;
Recuperación de la base de datos.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;
Ajústese a la zona horaria de Nueva York deseada por su usuario.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;
Puede ver todo el código ejecutado en vivo en IdeOne.com .
Por cierto, el seguimiento de eventos pasados también se trata como un momento. Cuándo llegó realmente el paciente a la cita, cuándo pagó la factura el cliente, cuándo firmó un nuevo empleado sus documentos, cuándo se bloqueó el servidor... todo esto se registra como un momento en UTC. Como se discutió anteriormente, por lo general sería un Instant
, aunque ZonedDateTime
&OffsetDateTime
también representan un momento. Para la base de datos use TIMESTAMP WITH TIME ZONE
(no WITHOUT
).
Hora del día
Espero que la mayoría de las aplicaciones orientadas a los negocios se centren en citas del otro tipo, en las que apuntamos a una cita con una hora del día en lugar de un momento específico.
Si el usuario hace una cita con su proveedor de atención médica para revisar los resultados de una prueba, lo hace para una hora particular del día en esa fecha. Si mientras tanto los políticos cambian las reglas de su zona horaria, adelantando o atrasando el reloj una hora o media hora o cualquier otra cantidad de tiempo, la fecha y la hora del día de esa cita médica siguen siendo las mismas. En realidad, el punto de la línea de tiempo de la cita original se cambiará después de que los políticos cambien la zona horaria, pasando a un punto anterior/posterior en la línea de tiempo.
Para dichas citas, no almacenar la fecha y la hora del día como se ve en UTC. Nosotros no use el tipo de columna de la base de datos TIMESTAMP WITH TIME ZONE
.
Para tales citas, almacenamos la fecha con la hora del día sin tener en cuenta la zona horaria. Usamos una columna de base de datos de tipo TIMESTAMP WITHOUT TIME ZONE
(nota WITHOUT
en lugar de WITH
). El tipo coincidente en Java es LocalDateTime
.
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;
Escríbelo en la base de datos.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;
Sea claro en esto:un LocalDateTime
el objeto no representar un momento, es no un punto específico en la línea de tiempo. Un LocalDateTime
objeto representa un rango de posibles momentos a lo largo de aproximadamente 26-27 horas de la línea de tiempo (el rango de zonas horarias alrededor del mundo). Para dar un significado real a un LocalDateTime
, debemos asociar una zona horaria deseada.
Para esa zona horaria deseada, use una segunda columna para almacenar el identificador de zona. Por ejemplo, las cadenas Europe/Rome
o America/New_York
. Ver lista de nombres de zonas
.
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
Escríbelo en la base de datos como texto.
myPreparedStatement.setString( … , zoneEuropeRome ) ;
Recuperación. Recuperar el nombre de la zona como texto e instanciar un ZoneId
objeto.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;
Junta esas dos piezas para determinar un momento representado como ZonedDateTime
objeto. Haga esto dinámicamente cuando necesite programar un calendario. Pero no almacenar el momento. Si los políticos redefinen la(s) zona(s) horaria(s) en el futuro, entonces se debe calcular un momento diferente.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;
El usuario viaja a Nueva York, EE. UU. Necesitan saber cuándo llamar al proveedor de atención médica en Milán, Italia, de acuerdo con los relojes de pared en su ubicación temporal de Nueva York. Así que ajústese de una zona horaria a otra. Mismo momento, diferente hora del reloj de pared.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;
datos de tz
Tenga en cuenta que si las reglas de las zonas horarias deseadas pueden estar cambiando, debe actualizar la copia de las definiciones de zona horaria en sus computadoras.
Java contiene su propia copia de tzdata , al igual que el motor de base de datos de Postgres. Y su sistema operativo host también. Este código en particular que se muestra aquí requiere que solo Java esté actualizado. Si usa Postgres para hacer ajustes de zona horaria, su tzdata también debe estar actualizado. Y para iniciar sesión y demás, su sistema operativo host debe mantenerse actualizado. Para que el usuario observe correctamente el reloj, el sistema operativo de su máquina cliente también debe estar actualizado.
Cuidado:los políticos de todo el mundo han mostrado una tendencia a cambiar sus zonas horarias con una frecuencia sorprendente y, a menudo, sin previo aviso.
Acerca de java.time
El java.tiempo
framework está integrado en Java 8 y versiones posteriores. Estas clases reemplazan al viejo y problemático legacy
clases de fecha y hora como java.util.Date
, Calendar
, &SimpleDateFormat
.
Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
El Joda-Time proyecto, ahora en modo de mantenimiento , recomienda la migración a java.hora clases.
Puede intercambiar java.time objetos directamente con su base de datos. Use un controlador JDBC
compatible con JDBC 4.2
o después. Sin necesidad de cadenas, sin necesidad de java.sql.*
clases Hibernate 5 y JPA 2.2 admiten java.time .
¿Dónde obtener las clases java.time?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
, y posteriores:parte de la API de Java estándar con una implementación integrada.
- Java 9 trajo algunas características y correcciones menores.
- Java SE 6
y Java SE 7
- La mayor parte del java.time la funcionalidad está adaptada a Java 6 y 7 en ThreeTen-Backport .
- Android
- Las versiones posteriores de Android (26+) incluyen implementaciones de java.time clases.
- Para Android anterior (<26), un proceso conocido como Desazucarado de API
trae un subconjunto de java.time
funcionalidad no integrada originalmente en Android.
- Si la eliminación de azúcar no ofrece lo que necesita, el ThreeTenABP proyecto adapta ThreeTen-Backport (mencionado anteriormente) a Android. Consulte Cómo usar ThreeTenABP… .