Tengo que almacenar UTC dateTime en DB.
He convertido el dateTime dado en una zona horaria específica a UTC. para eso seguí el siguiente código.
Mi fecha y hora de entrada es "20121225 10:00:00 Z" La zona horaria es "Asia / Calcuta"
Mi servidor / DB (Oracle) se está ejecutando en la misma zona horaria (IST) "Asia / Calcutta"
Obtenga el objeto Date en esta zona horaria específica
String date = "20121225 10:00:00 Z";
String timeZoneId = "Asia/Calcutta";
TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
//This date object is given time and given timezone
java.util.Date parsedDate = dateFormatLocal.parse(date + " "
+ timeZone.getDisplayName(false, TimeZone.SHORT));
if (timeZone.inDaylightTime(parsedDate)) {
// We need to re-parse because we don't know if the date
// is DST until it is parsed...
parsedDate = dateFormatLocal.parse(date + " "
+ timeZone.getDisplayName(true, TimeZone.SHORT));
}
//assigning to the java.sql.TimeStamp instace variable
obj.setTsSchedStartTime(new java.sql.Timestamp(parsedDate.getTime()));
Almacenar en DB
if (tsSchedStartTime != null) {
stmt.setTimestamp(11, tsSchedStartTime);
} else {
stmt.setNull(11, java.sql.Types.DATE);
}
SALIDA
DB (Oracle) ha almacenado lo mismo dado que dateTime: "20121225 10:00:00
no está en UTC.
Lo he confirmado desde el siguiente sql.
select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable
Mi servidor de base de datos también se ejecuta en la misma zona horaria "Asia / Calcuta"
Me da las siguientes apariencias
Date.getTime()
no está en UTC- O bien, la marca de tiempo tiene un impacto en la zona horaria mientras se almacena en la base de datos. ¿Qué estoy haciendo mal aquí?
Una pregunta más:
¿Se timeStamp.toString()
imprimirá en la zona horaria local como java.util.date
hace? ¿No es UTC?
java.sql.Timestamp
está en UTC, cuando se almacena en un campo TIMESTAMP sin información de zona horaria, usa la fecha / hora equivalente en la zona horaria actual de la máquina virtualRespuestas:
Aunque no se especifica explícitamente, los
setTimestamp(int parameterIndex, Timestamp x)
controladores deben seguir las reglas establecidas por elsetTimestamp(int parameterIndex, Timestamp x, Calendar cal)
javadoc :Cuando llama con
setTimestamp(int parameterIndex, Timestamp x)
el controlador JDBC, utiliza la zona horaria de la máquina virtual para calcular la fecha y hora de la marca de tiempo en esa zona horaria. Esta fecha y hora es lo que se almacena en la base de datos, y si la columna de la base de datos no almacena la información de la zona horaria, entonces cualquier información sobre la zona se pierde (lo que significa que depende de las aplicaciones que utilizan la base de datos para utilizar la misma zona horaria consistentemente o idear otro esquema para discernir la zona horaria (es decir, almacenar en una columna separada).Por ejemplo: su zona horaria local es GMT + 2. Almacena "2012-12-25 10:00:00 UTC". El valor real almacenado en la base de datos es "2012-12-25 12:00:00". Lo recuperas de nuevo: lo recuperas como "2012-12-25 10:00:00 UTC" (pero solo si lo recuperas usando
getTimestamp(..)
), pero cuando otra aplicación accede a la base de datos en la zona horaria GMT + 0, lo hará recuperar la marca de tiempo como "2012-12-25 12:00:00 UTC".Si desea almacenarlo en una zona horaria diferente, debe usar el
setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
con una instancia de Calendario en la zona horaria requerida. Solo asegúrese de usar también el getter equivalente con la misma zona horaria al recuperar valores (si usa unTIMESTAMP
sin información de zona horaria en su base de datos).Entonces, asumiendo que desea almacenar la zona horaria GMT real, debe usar:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); stmt.setTimestamp(11, tsSchedStartTime, cal);
Con JDBC 4.2, un controlador compatible debería admitir
java.time.LocalDateTime
(yjava.time.LocalTime
) paraTIMESTAMP
(yTIME
) hastaget/set/updateObject
. Lasjava.time.Local*
clases no tienen zonas horarias, por lo que no es necesario aplicar ninguna conversión (aunque eso podría abrir un nuevo conjunto de problemas si su código asumiera una zona horaria específica).fuente
toString()
encendidojava.lang.Date
(yjava.lang.Timestamp
) siempre lo presentará en su zona horaria local.Creo que la respuesta correcta debería ser java.sql.Timestamp NO es específico de la zona horaria. La marca de tiempo es una combinación de java.util.Date y un valor de nanosegundos separado. No hay información de zona horaria en esta clase. Así, al igual que Date, esta clase simplemente contiene el número de milisegundos desde el 1 de enero de 1970, 00:00:00 GMT + nanos.
En PreparedStatement.setTimestamp (int parameterIndex, Timestamp x, Calendar cal), el controlador utiliza el calendario para cambiar la zona horaria predeterminada. Pero la marca de tiempo todavía tiene milisegundos en GMT.
La API no está clara acerca de cómo se supone que el controlador JDBC debe usar el Calendario. Los proveedores parecen sentirse libres sobre cómo interpretarlo, por ejemplo, la última vez que trabajé con MySQL 5.5 Calendar, el controlador simplemente ignoró Calendar en PreparedStatement.setTimestamp y ResultSet.getTimestamp.
fuente
Para Mysql, tenemos una limitación. En el controlador Mysql doc , tenemos:
useTimezone=true useLegacyDatetimeCode=false serverTimezone=UTC
Entonces, cuando no usamos estos parámetros y llamamos
setTimestamp or getTimestamp
con calendario o sin calendario, tenemos la marca de tiempo en la zona horaria de jvm.Ejemplo:
La zona horaria de jvm es GMT + 2. En la base de datos, tenemos una marca de tiempo: 1461100256 = 19/04/16 21: 10: 56,000000000 GMT
Properties props = new Properties(); props.setProperty("user", "root"); props.setProperty("password", ""); props.setProperty("useTimezone", "true"); props.setProperty("useLegacyDatetimeCode", "false"); props.setProperty("serverTimezone", "UTC"); Connection con = DriverManager.getConnection(conString, props); ...... Calendar nowGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT")); Calendar nowGMTPlus4 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4")); ...... rs.getTimestamp("timestampColumn");//Oracle driver convert date to jvm timezone and Mysql convert date to GMT (specified in the parameter) rs.getTimestamp("timestampColumn", nowGMT);//convert date to GMT rs.getTimestamp("timestampColumn", nowGMTPlus4);//convert date to GMT+4 timezone
El primer método devuelve: 1461100256000 = 19/04/2016 - 21:10:56 GMT
El segundo método devuelve: 1461100256000 = 19/04/2016 - 21:10:56 GMT
El tercer método devuelve: 1461085856000 = 19/04/2016 - 17:10:56 GMT
En lugar de Oracle, cuando usamos las mismas llamadas, tenemos:
El primer método devuelve: 1461093056000 = 19/04/2016 - 19:10:56 GMT
El segundo método devuelve: 1461100256000 = 19/04/2016 - 21:10:56 GMT
El tercer método devuelve: 1461085856000 = 19/04/2016 - 17:10:56 GMT
NB: No es necesario especificar los parámetros para Oracle.
fuente
ALTER SESSION SET TIME_ZONE = 'UTC'
".Es específico de su conductor. Debe proporcionar un parámetro en su programa Java para indicarle la zona horaria que desea utilizar.
java -Duser.timezone="America/New_York" GetCurrentDateTimeZone
Además de esto:
to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')
También puede ser útil para manejar la conversión correctamente. Tomado de aqui
fuente
La respuesta es que
java.sql.Timestamp
es un desastre y debe evitarse. Úselo en sujava.time.LocalDateTime
lugar.Entonces, ¿por qué es un desastre? Desde
java.sql.Timestamp
JavaDoc, ajava.sql.Timestamp
es una "envoltura delgadajava.util.Date
que permite que la API de JDBC identifique esto como un valor TIMESTAMP de SQL". Desdejava.util.Date
JavaDoc, "laDate
clase está destinada a reflejar la hora universal coordinada (UTC)". De la especificación ISO SQL, TIMESTAMP WITHOUT TIME ZONE "es un tipo de datos que es fecha y hora sin zona horaria". TIMESTAMP es un nombre corto para TIMESTAMP SIN HUSO HORARIO. Entonces, unjava.sql.Timestamp
"refleja" UTC mientras que SQL TIMESTAMP es "sin zona horaria".Debido a que
java.sql.Timestamp
refleja UTC, sus métodos aplican conversiones. Esto causa una confusión sin fin. Desde la perspectiva de SQL, no tiene sentido convertir un valor de TIMESTAMP de SQL a otra zona horaria, ya que TIMESTAMP no tiene una zona horaria desde la que convertir. ¿Qué significa convertir 42 a Fahrenheit? No significa nada porque 42 no tiene unidades de temperatura. Es solo un número desnudo. De manera similar, no puede convertir un TIMESTAMP de 2020-07-22T10: 38: 00 a Americas / Los Angeles porque 2020-07-22T10: 30: 00 no está en ninguna zona horaria. No está en UTC o GMT ni nada más. Es una hora de cita desnuda.java.time.LocalDateTime
también es una fecha y hora desnuda. No tiene una zona horaria, exactamente como SQL TIMESTAMP. Ninguno de sus métodos aplica ningún tipo de conversión de zona horaria, lo que hace que su comportamiento sea mucho más fácil de predecir y comprender. Así que no lo usejava.sql.Timestamp
. Utilicejava.time.LocalDateTime
.LocalDateTime ldt = rs.getObject(col, LocalDateTime.class); ps.setObject(param, ldt, JDBCType.TIMESTAMP);
fuente
Puede utilizar el método siguiente para almacenar la marca de tiempo en la base de datos específica para su zona / identificación de zona deseada.
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta")) ; Timestamp timestamp = Timestamp.valueOf(zdt.toLocalDateTime());
Un error común que la gente comete es
LocaleDateTime
para obtener la marca de tiempo de ese instante, lo que descarta cualquier información específica de su zona, incluso si intenta convertirla más tarde. No comprende la Zona.Tenga en cuenta que
Timestamp
es de la clasejava.sql.Timestamp
.fuente