java - localdatetime - ¿Cómo convertir ZonedDateTime a la fecha?
new zoneddatetime java (8)
Estoy tratando de establecer una fecha y hora agnóstica del servidor en mi base de datos y creo que la mejor práctica para hacerlo es establecer una fecha y hora UTC. Mi servidor db es Cassandra y el controlador db para Java solo comprende el tipo de fecha.
Asumiendo que en mi código estoy usando el nuevo Java 8 ZonedDateTime para obtener el UTC ahora (
ZonedDateTime.now(ZoneOffset.UTC)
), ¿cómo puedo convertir esta instancia de ZonedDateTime a la clase de fecha "heredada"?
tl; dr
java.util.Date.from( // Transfer the moment in UTC, truncating any microseconds or nanoseconds to milliseconds.
Instant.now() ; // Capture current moment in UTC, with resolution as fine as nanoseconds.
)
Aunque no había ningún punto en ese código anterior.
Tanto
java.util.Date
como
Instant
representan un momento en UTC, siempre en UTC.
El código anterior tiene el mismo efecto que:
new java.util.Date() // Capture current moment in UTC.
No hay beneficio aquí para usar
ZonedDateTime
.
Si ya tiene un
ZonedDateTime
, ajústelo a UTC extrayendo un
Instant
.
java.util.Date.from( // Truncates any micros/nanos.
myZonedDateTime.toInstant() // Adjust to UTC. Same moment, same point on the timeline, different wall-clock time.
)
Otra respuesta correcta
La
Answer
de
ssoltanid
aborda correctamente su pregunta específica, cómo convertir un objeto java.time de la nueva escuela (
ZonedDateTime
) en un objeto
java.util.Date
vieja escuela.
Extraiga el
Instant
del ZonedDateTime y pase a
java.util.Date.from()
.
Pérdida de datos
Tenga en cuenta que
sufrirá pérdida de datos
, ya que
Instant
seguimiento de
nanoseconds
desde la
epoch
mientras que
java.util.Date
seguimiento de
milliseconds
desde la época.
Su pregunta y comentarios plantean otros problemas.
Mantener servidores en UTC
Sus servidores deben tener su sistema operativo host configurado en UTC como una práctica recomendada en general. La JVM recoge esta configuración del sistema operativo host como su zona horaria predeterminada, en las implementaciones de Java que conozco.
Especificar zona horaria
Pero nunca debe confiar en la zona horaria predeterminada actual de la JVM.
En lugar de seleccionar la configuración del host, se pasa una bandera al iniciar una JVM puede establecer otra zona horaria.
Peor aún: cualquier código en cualquier subproceso de cualquier aplicación en cualquier momento puede hacer una llamada a
java.util.TimeZone::setDefault
para cambiar ese valor predeterminado en tiempo de ejecución.
Tipo de
Timestamp
Cassandra
Cualquier
base de datos
y controlador
decentes
deben manejar automáticamente el ajuste de una fecha y hora pasada a UTC para el almacenamiento.
No uso Cassandra, pero parece tener algún soporte rudimentario para la fecha y hora.
La documentación dice que su tipo de
Timestamp
es un recuento de milisegundos de la misma época (primer momento de 1970 en UTC).
ISO 8601
Además, Cassandra acepta entradas de cadena en los formatos estándar
ISO 8601
.
Afortunadamente, java.time utiliza formatos ISO 8601 como valores predeterminados para analizar / generar cadenas.
La implementación de la clase
Instant
''
toString
bien.
Precisión: milisegundos vs nanosecord
Pero primero necesitamos reducir la precisión de nanosegundos de ZonedDateTime a milisegundos. Una forma es crear un Instante nuevo usando milisegundos. Afortunadamente, java.time tiene algunos métodos útiles para convertir ay desde milisegundos.
Código de ejemplo
Aquí hay un código de ejemplo en Java 8 Update 60.
ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
…
Instant instant = zdt.toInstant();
Instant instantTruncatedToMilliseconds = Instant.ofEpochMilli( instant.toEpochMilli() );
String fodderForCassandra = instantTruncatedToMilliseconds.toString(); // Example: 2015-08-18T06:36:40.321Z
O de acuerdo con este
documento del controlador Cassandra Java
, puede pasar una instancia de
java.util.Date
(que no debe confundirse con
java.sqlDate
).
Por lo tanto, puede hacer un juDate a partir de ese
instantTruncatedToMilliseconds
en el código anterior.
java.util.Date dateForCassandra = java.util.Date.from( instantTruncatedToMilliseconds );
Si haces esto a menudo, podrías hacer una frase.
java.util.Date dateForCassandra = java.util.Date.from( zdt.toInstant() );
Pero sería mejor crear un pequeño método de utilidad.
static public java.util.Date toJavaUtilDateFromZonedDateTime ( ZonedDateTime zdt ) {
Instant instant = zdt.toInstant();
// Data-loss, going from nanosecond resolution to milliseconds.
java.util.Date utilDate = java.util.Date.from( instant ) ;
return utilDate;
}
Observe la diferencia en todo este código que en la Pregunta. El código de la pregunta intentaba ajustar la zona horaria de la instancia ZonedDateTime a UTC. Pero eso no es necesario. Conceptualmente:
ZonedDateTime = Instant + ZoneId
Simplemente extraemos la parte Instant, que ya está en UTC (básicamente en UTC, lea el documento de la clase para obtener detalles precisos).
Sobre java.time
El marco
java.time
está integrado en Java 8 y
java.time
posteriores.
Estas clases suplantan a las antiguas y problemáticas clases de fecha y hora
legacy
, como
java.util.Date
,
Calendar
y
SimpleDateFormat
.
El proyecto Joda-Time , 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 para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
Puede intercambiar objetos
java.time
directamente con su base de datos.
Utilice un
controlador JDBC
compatible con
JDBC 4.2
o posterior.
No necesita cadenas, no necesita clases
java.sql.*
.
¿Dónde obtener las clases java.time?
-
Java SE 8
,
Java SE 9
,
Java SE 10
y posterior
- Incorporado.
- Parte de la API Java estándar con una implementación integrada.
- Java 9 agrega algunas características menores y correcciones.
-
Java SE 6
y
Java SE 7
- Gran parte de la funcionalidad java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
-
Android
- Versiones posteriores de las implementaciones de paquetes de Android de las clases java.time .
- Para Android anterior (<26), el proyecto ThreeTen-Backport adapta ThreeTen-Backport (mencionado anteriormente). Vea Cómo usar ThreeTenABP ...
El proyecto
ThreeTen-Extra
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
more
.
Aquí hay un ejemplo que convierte la hora actual del sistema a UTC. Implica formatear ZonedDateTime como String y luego el objeto String se analizará en un objeto date usando java.text DateFormat.
ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
final DateFormat FORMATTER_YYYYMMDD_HH_MM_SS = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
String dateStr = zdt.format(DATETIME_FORMATTER);
Date utcDate = null;
try {
utcDate = FORMATTER_YYYYMMDD_HH_MM_SS.parse(dateStr);
}catch (ParseException ex){
ex.printStackTrace();
}
Para una aplicación acoplada como beehuang comentó, debe configurar su zona horaria.
Alternativamente, puede usar conZoneSameLocal . Por ejemplo:
2014-07-01T00: 00 + 02: 00 [GMT + 02: 00] se convierte por
Date.from(zonedDateTime.withZoneSameLocal(ZoneId.systemDefault()).toInstant())
al martes 01 de julio 00:00:00 CEST 2014 y por
Date.from(zonedDateTime.toInstant())
al lunes 30 de junio 22:00:00 UTC 2014
Puede convertir ZonedDateTime a un instante, que puede usar directamente con Date.
Date.from(java.time.ZonedDateTime.now().toInstant());
Si está utilizando el
backport ThreeTen
para Android y no puede utilizar el
Date.from(Instant instant)
más
Date.from(Instant instant)
(que requiere un mínimo de API 26) puede usar:
ZonedDateTime zdt = ZonedDateTime.now();
Date date = new Date(zdt.toInstant().toEpochMilli());
o:
Date date = DateTimeUtils.toDate(zdt.toInstant());
Lea también los consejos en la respuesta de Basil Bourque
Si solo le interesa ahora, simplemente use:
Date d = new Date();
Yo uso esto.
public class TimeTools {
public static Date getTaipeiNowDate() {
Instant now = Instant.now();
ZoneId zoneId = ZoneId.of("Asia/Taipei");
ZonedDateTime dateAndTimeInTai = ZonedDateTime.ofInstant(now, zoneId);
try {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInTai.toString().substring(0, 19).replace("T", " "));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Porque
Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant());
¡¡¡No es trabajo!!!
Si ejecuta su aplicación en su computadora, no hay problema.
Pero si ejecuta en cualquier región de AWS o Docker o GCP, generará un problema.
Porque la computadora no es tu zona horaria en la nube.
Debe establecer su zona horaria correcta en el Código.
Por ejemplo, Asia / Taipei.
Luego corregirá en AWS o Docker o GCP.
public class App {
public static void main(String[] args) {
Instant now = Instant.now();
ZoneId zoneId = ZoneId.of("Australia/Sydney");
ZonedDateTime dateAndTimeInLA = ZonedDateTime.ofInstant(now, zoneId);
try {
Date ans = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInLA.toString().substring(0, 19).replace("T", " "));
System.out.println("ans="+ans);
} catch (ParseException e) {
}
Date wrongAns = Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant());
System.out.println("wrongAns="+wrongAns);
}
}