Solución:
tl; dr
Usa lo moderno java.time clases.
ZonedDateTime.now( // Capture the current moment in the wall-clock time used by the people of a certain region (a time zone).
ZoneId.systemDefault() // Get the JVM’s current default time zone. Can change at any moment during runtime. If important, confirm with the user.
) // Renders a `ZonedDateTime` object. To see the same moment in UTC, extract a `Instant` object by calling `ZonedDateTime::getInstant`.
Puede omitir la llamada explícita a ZoneId.systemDefault
. (Pero no recomiendo esto).
ZonedDateTime.now() // Capture the current moment in the JVM’s current default time zone.
Analiza tu cadena como LocalDateTime
y ajústelo a la zona horaria deseada.
LocalDateTime.parse( "2014-02-14T06:04:00:00" ) // Parse a string lacking any indicator of time zone or offset-from-UTC. *Not* a specific point on the timeline.
.atOffset( ZoneOffset.UTC ) // Apply UTC as we are certain that offset-from-UTC of zero was intended by the supplier of that input string. Returns a `OffsetDateTime` object.
.atZoneSameInstant( // Adjust into another time zone. The `sameInstant` part means the same moment, different wall-clock time.
ZoneId.of( "Africa/Tunis" ) // Specify the particular zone of interest to you.
) // Returns a `ZonedDateTime` object.
Evitar java.util.Date
Y .Calendar
Estas clases heredadas son notoriamente problemáticas. Sun / Oracle agregó el paquete java.time en Java 8 para reemplazarlos. Ese paquete se inspiró en Joda-Time.
Entre los problemas de las clases heredadas se encuentra este comportamiento confuso: si bien un java.util.Date no tiene información de zona horaria, es toString
La implementación aplica la zona horaria predeterminada actual de la JVM al generar una cadena. Por lo tanto, lo engaña al parecer tener una zona horaria cuando no la tiene.
java.time
Estoy intentando convertir esta cadena “2014-02-14T06: 04: 00: 00”,…
Su cadena de entrada carece de cualquier indicador de zona horaria o desplazamiento de UTC. Así que analizamos como LocalDateTime
, que carece de cualquier concepto de zona / compensación.
A LocalDateTime
lo hace no representar un momento, es no un punto en la línea de tiempo. La palabra “local” aquí no significa una localidad específica. Significa “ninguna localidad específica”. Sin el contexto de una zona / compensación, no tiene un significado real.
LocalDateTime ldt = LocalDateTime.parse( "2014-02-14T06:04:00:00" ) ;
… que está en GMT …
Dice que está seguro de que el proveedor de esa cadena de entrada pretendía UTC como contexto. Podemos aplicar un desplazamiento de UTC de cero, o el propio UTC, para obtener un OffsetDateTime
objeto. Un OffsetDateTime
es un momento, un punto en la línea de tiempo. Podemos especificar el ZoneOffset
usando la constante para UTC, ZoneOffset.UTC
.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
… a mi zona horaria datetime
Aparentemente, desea ajustar ese momento a otra zona horaria, para ver la hora del reloj de pared que usa la gente de una región en particular. Necesitamos aplicar una zona horaria (ZoneId
) conseguir un ZonedDateTime
.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = odt.atZone( z ) ;
En lugar de especificar una zona horaria, puede pedirle a su JVM su zona horaria predeterminada actual. Atención: la zona horaria predeterminada actual de la JVM se puede cambiar en cualquier momento mediante cualquier código en cualquier hilo de cualquier aplicación dentro de esa JVM.
ZoneId z = ZoneId.systemDefault() ;
ZonedDateTime zdt = odt.atZone( z ) ;
El punto es que mi aplicación se utilizará en diferentes ubicaciones geográficas.
Simplemente especifique sus zonas horarias deseadas / esperadas explícitamente. Este es siempre buena práctica, en mi opinión. La zona horaria predeterminada está fuera de su control como programador, lo que la hace poco confiable.
Especifique un nombre de zona horaria adecuado en el formato de continent/region
, tal como America/Montreal
, Africa/Casablanca
, o Pacific/Auckland
. Nunca use la abreviatura de 3-4 letras como EST
o IST
ya que son no zonas horarias verdaderas, no estandarizadas y ni siquiera únicas (!).
Otro consejo: trabaje, piense, almacene e intercambie en UTC. Olvídese de su propia zona horaria parroquial, ya que trasladarse de un lado a otro a su zona de origen lo volverá loco. Piense en UTC como el Una vez verdadera, y otras zonas / compensaciones son meras variaciones.
Instant instant = Instant.now() ; // Capture current moment in UTC.
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;
ZoneId zKolkata = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdtKolkata = instant.atZone( zKolkata ) ;
ZoneId zCasablanca = ZoneId.of( "Africa/Casablanca" ) ;
ZonedDateTime zdtCasablanca = instant.atZone( zCasablanca ) ;
Allí tenemos cuatro formas ( instant
, zdtAuckland
, zdtKolkata
, zdtCasablanca
) de mirar el mismo momento simultáneo, el mismo punto en la línea de tiempo.
instant.toString (): 2018-05-08T20: 55: 14.761721Z
zdtAuckland.toString (): 2018-05-09T08: 55: 14.761721 + 12: 00[Pacific/Auckland]
zdtKolkata.toString (): 2018-05-09T02: 25: 14.761721 + 05: 30[Asia/Kolkata]
zdtCasablanca.toString (): 2018-05-08T21: 55: 14.761721 + 01: 00[Africa/Casablanca]
Zona vs Desplazamiento
Un desplazamiento de UTC es simplemente una cantidad de horas, minutos y segundos. Nada más y nada menos. Cualquier número de zonas horarias puede compartir un desplazamiento particular en un momento particular.
Una zona horaria es un historial de cambios pasados, presentes y futuros en el desplazamiento utilizado por las personas de una región en particular. Por ejemplo, el horario de verano (DST) es una práctica en la que la gente de una región (inexplicablemente) decide cambiar su compensación dos veces al año.
Por lo tanto, siempre es preferible una zona horaria a una simple compensación. Tener una zona nos permite sumar o restar tiempo de una manera significativa, para tener en cuenta los cambios en la compensación en el historial de esa región.
Sobre java.time
los java.time framework está integrado en Java 8 y versiones posteriores. Estas clases suplantan las antiguas y problemáticas clases de fecha y hora heredadas, como java.util.Date
, Calendar
Y SimpleDateFormat
.
los Joda-Time proyecto, ahora en modo de mantenimiento, aconseja la migración a las clases java.time.
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.
Puedes intercambiar java.time objetos directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. Sin necesidad de cuerdas, sin necesidad de java.sql.*
clases.
¿Dónde obtener las clases java.time?
-
Java SE 8, Java SE 9, Java SE 10, y después
- Incorporado.
- Parte de la API estándar de Java con una implementación integrada.
- Java 9 agrega algunas características y correcciones menores.
-
Java SE 6 y Java SE 7
- Gran parte de la funcionalidad de java.time está retroportada a Java 6 y 7 en ThreeTen-Backport.
-
Androide
- Versiones posteriores de implementaciones de paquetes de Android de las clases java.time.
- Para Android anterior (<26), el ThreeTenABP el proyecto se adapta ThreeTen-Backport (mencionado anteriormente). Ver Cómo usar ThreeTenABP….
los ThreeTen-Extra el proyecto extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval
, YearWeek
, YearQuarter
, y más.
Joda-Time
ACTUALIZACIÓN: El proyecto Joda-Time ahora está en modo de mantenimiento. El equipo aconseja la migración a las clases java.time. Vaya a la sección java.time a continuación en esta respuesta.
El paquete Joda-Time tiene un buen soporte claro para las zonas horarias. A diferencia de java.util.Date, Joda-Time DateTime sí conoce su propia zona horaria asignada. Si no especifica una zona horaria, la zona horaria predeterminada actual de la JVM se asigna implícitamente.
DateTime dateTime = DateTime.now(); // Applies JVM’s default time zone implicitly.
Recomiendo no confiar implícitamente en la zona horaria predeterminada. Hacerlo genera confusión y errores al realizar el trabajo de fecha y hora.
DateTime dateTime = DateTime.now( DateTimeZone.getDefault() ); // Explicitly using default time zone.
Si es necesario, puede asignar una zona horaria.
DateTime dateTimeKolkata = DateTime.now( DateTimeZone.forID( "Asia/Kolkata" ) ); // Specify a time zone.
Para el trabajo del lado del servidor, la mejor práctica es hacer la lógica empresarial y el almacenamiento de la base de datos en UTC.
DateTime dateTimeUtc = DateTime.now( DateTimeZone.UTC ); // Assign UTC (GMT) time zone.
Puede convertir de la zona horaria asignada a otra, incluida la zona horaria predeterminada actual de la JVM.
DateTime dateTime = dateTimeUtc.withZone( DateTimeZone.getDefault() );
Inmutable
Para la seguridad de los subprocesos, Joda-Time utiliza objetos inmutables. En lugar de modificar un objeto, métodos como withZone
crea una nueva instancia basada en el original.
Analizar cadena
Para analizar una cadena como una fecha y hora, debe tener en cuenta si la cadena incluye un desplazamiento de UTC y / o una zona horaria. El tuyo no. Por lo tanto, debe especificar una zona horaria para interpretar esa cadena. Si no lo especifica, la zona horaria predeterminada actual de la JVM se utilizará durante el análisis.
En su pregunta, dijo que la cadena representa una fecha y hora en UTC (GMT).
DateTime dateTimeUtc = new DateTime( "2014-02-14T06:04:00:00", DateTimeZone.UTC );
Después del análisis, puede asignar otra zona horaria si es necesario. Mismo momento en la línea de tiempo del Universo, pero muestra una hora diferente en el Reloj de Pared.
DateTime dateTimeDefaultZone = dateTimeUtc.withZone( DateTimeZone.getDefault() );
Observe que este fue un proceso de dos pasos. Primero analizamos su Cadena utilizando nuestro conocimiento externo de la zona horaria prevista de esa Cadena porque carecía de representación interna de esa zona horaria o desplazamiento. En segundo lugar, ajustamos la zona horaria a otra (la zona predeterminada de JVM).
Si su cadena hubiera incluido un desplazamiento de +00:00
o el acostumbrado Z
, podríamos haber derrumbado esos dos pasos en uno.
DateTime dateTimeDefaultZone = new DateTime( "2014-02-14T06:04:00:00Z", DateTimeZone.getDefault() ); // Apply time zone adjustment *after* parsing.
Tenga en cuenta que este constructor de DateTime se parece al anterior, pero en realidad es bastante diferente. Se aplica el argumento de la zona horaria de este después analizar, en lugar de durante análisis. Aquí, el argumento de la zona horaria se utiliza para ajustar el DateTime ya analizado. Ese Z
al final hace un mundo de diferencia.
Fuente de la zona horaria predeterminada
La JVM inicialmente obtiene su zona horaria predeterminada del sistema operativo host. Pero tenga en cuenta que un programador puede anular esto mediante:
- Pase un argumento en la línea de comandos al iniciar la JVM.
- Llama
java.util.TimeZone.setDefault
.
Hacer esta anulación afecta a todos los subprocesos de todas las aplicaciones que se ejecutan en esa JVM. Por lo tanto, debe saber que la zona horaria predeterminada de la JVM suele ser la misma que la del sistema operativo host, pero no necesariamente la misma.
Aquí hay una forma de obtener la identificación de un TimeZone
que coincida con la compensación del reloj del sistema local,
Calendar cal = Calendar.getInstance();
long milliDiff = cal.get(Calendar.ZONE_OFFSET);
// Got local offset, now loop through available timezone id(s).
String [] ids = TimeZone.getAvailableIDs();
String name = null;
for (String id : ids) {
TimeZone tz = TimeZone.getTimeZone(id);
if (tz.getRawOffset() == milliDiff) {
// Found a match.
name = id;
break;
}
}
System.out.println(name);