Después de de una larga recopilación de datos resolvimos esta traba que tienen muchos de nuestros lectores. Te regalamos la solución y nuestro objetivo es que resulte de mucha ayuda.
Solución:
Cambié la zona horaria de mi computadora a Europa / Bucarest para un experimento. Esto es UTC + 2 horas como su zona horaria.
Ahora, cuando copio tu código, obtengo un resultado similar al tuyo:
Instant now = Instant.now();
System.out.println(now); // prints 2017-03-14T06:16:32.621Z
Timestamp current = Timestamp.from(now);
System.out.println(current); // 2017-03-14 08:16:32.621
La salida se da en los comentarios. Sin embargo, continúo:
DateFormat df = DateFormat.getDateTimeInstance();
df.setTimeZone(TimeZone.getTimeZone("UTC"));
// the following prints: Timestamp in UTC: 14-03-2017 06:16:32
System.out.println("Timestamp in UTC: " + df.format(current));
Ahora puedes ver que el Timestamp
realmente está de acuerdo con el Instant
comenzamos desde (solo los milisegundos no se imprimen, pero confío en que también estén allí). Así que ha hecho todo correctamente y solo se confundió porque cuando imprimimos el Timestamp
estábamos llamando implícitamente a su toString
método, y este método a su vez toma la configuración de la zona horaria de la computadora y muestra la hora en esta zona. Solo por esto, las pantallas son diferentes.
La otra cosa que intentaste, usando LocalDateTime
, parece funcionar, pero realmente no te da lo que quieres:
LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC);
System.out.println(ldt); // 2017-03-14T06:16:32.819
current = Timestamp.valueOf(ldt);
System.out.println(current); // 2017-03-14 06:16:32.819
System.out.println("Timestamp in UTC: " + df.format(current)); // 14-03-2017 04:16:32
Ahora, cuando imprimimos el Timestamp
usando nuestro UTC DateFormat
, podemos ver que es 2 horas antes, 04:16:32 UTC cuando el Instant
son las 06:16:32 UTC. Así que este método es engañoso, parece que funciona, pero no es así.
Esto muestra el problema que condujo al diseño de las clases de fecha y hora de Java 8 para reemplazar las antiguas. Entonces, la verdadera y buena solución a su problema probablemente sería conseguir un controlador JDBC 4.2 que pueda aceptar un Instant
objeto fácilmente para que pueda evitar la conversión a Timestamp
en total. No sé si eso está disponible para usted todavía, pero estoy convencido de que lo estará.
Clases incorrectas para usar
LocalDateTime ldt = LocalDateTime.ofInstant(Instnant.now(), ZoneOffset.UTC);
Timestamp current = Timestamp.valueOf(ldt);
Dos problemas con ese código.
En primer lugar, nunca mezcle lo moderno java.time clasesLocalDateTime
aquí) con las terribles clases antiguas de fecha y hora heredadas (java.sql.Timestamp
aquí). los java.time framework suplanta por completo las terribles clases antiguas, a partir de la adopción de JSR 310. Nunca necesitas usar Timestamp
de nuevo: A partir de JDBC 4.2 podemos intercambiar directamente java.time objetos con la base de datos.
El otro problema es que el LocalDateTime
la clase no puede, por definición, representar un momento. A propósito, carece de una zona horaria o un desplazamiento de UTC. Usar LocalDateTime
solo cuando te refieres a una fecha con la hora del día En todas partes o en cualquier sitio, en otras palabras, cualquiera / todos de muchos más momentos en un rango de aproximadamente 26-27 horas (los extremos actuales de las zonas horarias alrededor del mundo).
Hacer no usar LocalDateTime
cuando te refieres a un momento específico, un punto específico en la línea de tiempo. En su lugar use:
Instant
(siempre en UTC)OffsetDateTime
(una fecha con hora del día y con compensación de UTC)ZonedDateTime
(una fecha con la hora del día y con una zona horaria).
Luego trato de crear un objeto Timestamp
No lo hagas.
Nunca usar java.sql.Timestamp
. Reemplazado por java.time.Instant
. Siga leyendo para obtener más información.
Momento actual
Para capturar el momento actual en UTC, use cualquiera de estos:
Instant instant = Instant.now() ;
…o…
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
Ambos representan exactamente lo mismo, un momento en UTC.
Base de datos
Aquí hay un ejemplo de SQL y el código Java para pasar el momento actual a la base de datos.
El ejemplo usa el motor de base de datos H2, construido en Java.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; )
String name = "whatever";
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
Aquí hay una aplicación de ejemplo completa que usa ese código.
package com.basilbourque.example;
import java.sql.*;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.UUID;
public class MomentIntoDatabase
public static void main ( String[] args )
MomentIntoDatabase app = new MomentIntoDatabase();
app.doIt();
private void doIt ( )
try
Class.forName( "org.h2.Driver" );
catch ( ClassNotFoundException e )
e.printStackTrace();
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:moment_into_db_example_" ) ;
Statement stmt = conn.createStatement() ;
)
String sql = "CREATE TABLE event_ (n" +
" id_ UUID DEFAULT random_uuid() PRIMARY KEY ,n" +
" name_ VARCHAR NOT NULL ,n" +
" when_ TIMESTAMP WITH TIME ZONE NOT NULLn" +
") ; ";
System.out.println( sql );
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; )
String name = "whatever";
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
// Query all.
sql = "SELECT * FROM event_ ;";
try ( ResultSet rs = stmt.executeQuery( sql ) ; )
while ( rs.next() ) when: " + odt );
catch ( SQLException e )
e.printStackTrace();
Analizando string
Con respecto a un comentario relacionado de Melnyk, aquí hay otro ejemplo basado en el código de ejemplo anterior. En lugar de capturar el momento actual, este código analiza un string.
La entrada string carece de cualquier indicador de zona horaria o desplazamiento de UTC. Así que analizamos como LocalDateTime
, teniendo en cuenta que esto no representar un momento, es no un punto en la línea de tiempo.
String input = "22.11.2018 00:00:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );
ldt.toString (): 2018-11-22T00: 00
Pero se nos ha informado string estaba destinado a representar un momento en UTC, pero el remitente cometió un error y no incluyó esa información (como un Z
o +00:00
al final para significar UTC). Entonces, podemos aplicar un desplazamiento de UTC de cero horas-minutos-segundos para determinar un momento real, un punto específico en la línea de tiempo. El resultado como OffsetDateTime
objeto.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
odt.toString (): 2018-11-22T00: 00Z
los Z
al final significa UTC y se pronuncia “Zulu”. Definido en la norma ISO 8601.
Ahora que tenemos un momento en la mano, podemos enviarlo a la base de datos en una columna de tipo estándar SQL TIMESTAMP WITH TIME ZONE
.
preparedStatement.setObject( 2 , odt );
Cuando recupere ese valor almacenado.
OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );
2018-11-22T00: 00Z
Aquí está el completo para esta aplicación de ejemplo.
package com.basilbourque.example;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
public class MomentIntoDatabase
public static void main ( String[] args )
MomentIntoDatabase app = new MomentIntoDatabase();
app.doIt();
private void doIt ( )
try
Class.forName( "org.h2.Driver" );
catch ( ClassNotFoundException e )
e.printStackTrace();
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:moment_into_db_example_" ) ;
Statement stmt = conn.createStatement() ;
)
String sql = "CREATE TABLE event_ (n" +
" id_ UUID DEFAULT random_uuid() PRIMARY KEY ,n" +
" name_ VARCHAR NOT NULL ,n" +
" when_ TIMESTAMP WITH TIME ZONE NOT NULLn" +
") ; ";
System.out.println( sql );
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; )
String name = "whatever";
String input = "22.11.2018 00:00:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );
System.out.println( "ldt.toString(): " + ldt );
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
System.out.println( "odt.toString(): " + odt );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
// Query all.
sql = "SELECT * FROM event_ ;";
try ( ResultSet rs = stmt.executeQuery( sql ) ; )
while ( rs.next() ) name: " + name + "
catch ( SQLException e )
e.printStackTrace();
Mudado
Si debe interoperar con un código antiguo que aún no se ha actualizado para java.time, puede convertir de un lado a otro. Busque nuevos métodos to…
/from…
añadido a las clases antiguas.
Para conseguir un legado java.sql.Timestamp
objeto, llamar Timestamp.from( Instant )
. Para conseguir un Instant
De nuestros OffsetDateTime
objeto visto arriba, simplemente llame OffsetDateTime::toInstant
.
java.sql.Timestamp ts = Timestamp.from( odt.toInstant() ) ;
Yendo en la otra dirección.
OffsetDateTime odt = OffsetDateTime.ofInstant( ts.toInstant() , ZoneOffset.UTC ) ;
Si usa el ThreeTen-Backport biblioteca para proyectos de Java 6 y 7, consulte la DateTimeUtils
clase para el to…
/from…
métodos de conversión.
Sobre java.time
los java.time framework está integrado en Java 8 y versiones posteriores. Estas clases suplantan a 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, Java SE 11y posterior: 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
- La mayoría de java.time la funcionalidad está respaldada a Java 6 y 7 en ThreeTen-Backport.
- Androide
- Versiones posteriores de implementaciones de paquetes de Android del java.time clases.
- 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.
Acuérdate de que tienes autorización de agregar una reseña .