Lea la Pregunta Prácticas recomendadas para el horario de verano y la zona horaria. El tuyo es básicamente un duplicado.
Servidores en UTC
Sí, generalmente los servidores deben tener su sistema operativo establecido en UTC como zona horaria, o si no se proporciona, use GMT
o la zona horaria de Reykjavík Islandia. Es probable que su implementación de Java elija esta configuración como su propia zona horaria predeterminada actual.
Especifique la zona horaria
Pero no dependa de que la zona horaria esté configurada en UTC. Un administrador de sistemas podría cambiarlo. Y cualquier código Java en cualquier subproceso de cualquier aplicación dentro de su JVM puede cambiar la zona horaria predeterminada actual de la JVM en tiempo de ejecución llamando a TimeZone.setDefault
. Por lo tanto, acostúmbrese a especificar siempre la zona horaria deseada/esperada pasando el argumento opcional en su código Java.
Considero que es una falla de diseño que cualquier marco de fecha y hora haga que la zona horaria sea opcional. Ser opcional crea infinitas cantidades de confusión porque los programadores, como todos los demás, inconscientemente piensan en términos de su propia zona horaria personal a menos que se les solicite. Entonces, con demasiada frecuencia, en el trabajo de fecha y hora no se presta atención al problema. Agregue el problema de que el valor predeterminado de JVM varía. Por cierto, lo mismo para Locale
, los mismos problemas, siempre deben especificarse explícitamente.
UTC
Su lógica empresarial, el almacenamiento de datos y el intercambio de datos casi siempre deben realizarse en UTC. Casi todas las bases de datos tienen una función para ajustar cualquier entrada en UTC y almacenar en UTC.
Al presentar una fecha y hora a un usuario, ajústese a la zona horaria esperada. Al serializar un valor de fecha y hora, utilice los formatos de cadena ISO 8601. Consulte la respuesta de VickyArora para Oracle específicamente (soy una persona de Postgres). Asegúrese de leer el documento detenidamente y practique experimentando para comprender completamente el comportamiento de su base de datos. La especificación de SQL no explica mucho al respecto y el comportamiento varía ampliamente.
java.sql
Recuerde que cuando use Java y JDBC, usará java.sql.Timestamp
y tipos de datos relacionados. Siempre están en UTC, automáticamente. En el futuro, espere ver los controladores JDBC actualizados para usar directamente los nuevos tipos de datos definidos en el marco java.time integrado en Java 8 y versiones posteriores.
java.tiempo
Las clases antiguas están desfasadas por java.time. Aprende a usar java.time mientras evita el antiguo java.util.Date/.Calendar y hace que su vida de programación sea mucho más agradable.
Hasta que se actualice su controlador JDBC, puede usar los métodos convenientes de conversión integrados en java.time. Vea ejemplos a continuación, donde Instant
es un momento en UTC y ZonedDateTime
es un instante ajustado a una zona horaria.
Instant instant = myJavaSqlTimestamp.toInstant();
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Para ir en la otra dirección.
java.sql.Timestamp myJavaSqlTimestamp = java.sql.Timestamp.from( zdt.toInstant() );
Si necesita la zona horaria original, guárdela
Si los requisitos de su negocio consideran que la zona horaria de los datos de entrada originales es importante, para recordarla, guárdela explícitamente como una columna separada en la tabla de su base de datos. Puede usar un desplazamiento desde UTC, pero eso no proporciona información completa. Una zona horaria es un desplazamiento más un conjunto de reglas para el manejo pasado, presente y futuro de anomalías como el horario de verano. Por lo tanto, un nombre de zona horaria adecuado es el más apropiado, como America/Montreal
.
Solo la fecha es ambigua
Dijo que recopila muchos valores de solo fecha, sin hora del día y sin zona horaria. La clase para eso en java.time es LocalDate
. Como con LocalTime
y LocalDateTime
, la parte "Local..." significa que no hay una localidad en particular, por lo tanto, no hay zona horaria y, por lo tanto, no es un punto en la línea de tiempo, no tiene un significado real.
Tenga en cuenta que un valor de solo fecha es ambiguo por definición. En cualquier momento dado, la fecha varía en todo el mundo. Por ejemplo, justo después de la medianoche en París Francia es un nuevo día, pero en Montreal Québec la fecha sigue siendo "ayer".
Por lo general, en los negocios, alguna zona horaria está implícita, incluso se intuye inconscientemente. La intuición inconsciente sobre los puntos de datos tiende a no funcionar bien a largo plazo, especialmente en el software. Es mejor hacer explícito qué zona horaria se pretendía. Puede almacenar la zona deseada junto con la fecha, como otra columna en la tabla de la base de datos, o puede hacer un comentario en su código de programación. Creo que sería mucho mejor y más seguro almacenar un valor de fecha y hora. Entonces, ¿cómo transformamos una fecha-solo en una fecha-hora?
A menudo, un nuevo día es el momento después de la medianoche, el primer momento del día. Podrías pensar que significa la hora del día 00:00:00.0
pero no siempre. El horario de verano (DST) y posiblemente otras anomalías pueden empujar el primer momento a una hora de reloj de pared diferente. Deje que java.time determine la hora del día correcta para el primer momento pasando por LocalDate
clase y su atStartOfDay
método.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
ZonedDateTime todayStart = today.atStartOfDay( zoneId );
En algunos contextos comerciales, se puede definir (o suponer) un nuevo día como horario comercial. Por ejemplo, digamos que un editor en Nueva York quiere decir las 9 a. m. en su hora local cuando dice "el borrador del libro vence el 2 de enero". Obtengamos la hora del día para esa fecha en esa zona horaria.
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.of( 2016 , 1 , 2 , 9 , 0 , 0 , 0 , zoneId );
¿Qué significa eso para el autor que trabaja en Nueva Zelanda? Ajústese a su zona horaria particular para presentarle llamando a withZoneSameInstant
.
ZoneId zoneId_Pacific_Auckland = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt_Pacific_Auckland = zdt.withZoneSameInstant( zoneId_Pacific_Auckland );
Base de datos
Para el almacenamiento de la base de datos, nos transformamos en un Instant
(un momento en la línea de tiempo en UTC) y pase como java.sql.Timestamp
como se vio anteriormente.
java.sql.Timestamp ts = java.sql.Timestamp.from( zdt.toInstant() );
Cuando se recupere de la base de datos, vuelva a transformarse en una fecha y hora de Nueva York. Convertir desde java.sql.Timestamp
a un Instant
, luego aplique una zona horaria ZoneId
para obtener un ZonedDateTime
.
Instant instant = ts.toInstant();
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Si su controlador de base de datos cumple con JDBC 4.2 o posterior, es posible que pueda pasar/obtener los tipos java.time directamente en lugar de convertirlos a/desde tipos java.sql. Pruebe el PreparedStatement::setObject
y ResultSet::getObject
métodos.