¿Es java.sql.Timestamp la zona horaria específica?
oracle date (4)
Aunque no se especifica explícitamente para setTimestamp(int parameterIndex, Timestamp x)
controladores deben seguir las reglas establecidas por setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
javadoc :
Establece el parámetro designado en el valor
java.sql.Timestamp
dado, utilizando el objetoCalendar
dado. El controlador utiliza el objetoCalendar
para construir un valor SQLTIMESTAMP
, que luego el controlador envía a la base de datos. Con un objeto deCalendar
, el controlador puede calcular la marca de tiempo teniendo en cuenta una zona horaria personalizada. Si no se especifica ningún objeto deCalendar
, el controlador utiliza la zona horaria predeterminada, que es la de la máquina virtual que ejecuta la aplicación.
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 la hora de la marca de tiempo en esa zona horaria. Esta fecha y hora es lo que está almacenado en la base de datos, y si la columna de la base de datos no almacena información de zona horaria, se perderá toda la información sobre la zona (lo que significa que depende de la (s) aplicación (es) usar la base de datos misma zona horaria consistentemente o proponer otro esquema para discernir la zona horaria (es decir, almacenar en una columna separada).
Por ejemplo: su zona horaria local es GMT + 2. Usted 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 nuevamente: lo recuperas nuevamente 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 el huso horario GMT +0, recuperará la marca de tiempo como "2012-12-25 12:00:00 UTC".
Si desea almacenarlo en una zona horaria diferente, debe usar setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
con una instancia de Calendar en la zona horaria requerida. Solo asegúrate de utilizar también el getter equivalente con el mismo huso horario al recuperar valores (si usas TIMESTAMP
sin información de zona horaria en tu base de datos).
Entonces, suponiendo que quiera almacenar la zona horaria GMT real, necesita usar:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stmt.setTimestamp(11, tsSchedStartTime, cal);
Con JDBC 4.2, un controlador compatible debería ser compatible con java.time.LocalDateTime
(y java.time.LocalTime
) para TIMESTAMP
(y TIME
) a través de get/set/updateObject
. Las clases java.time.Local*
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 asumió un huso horario específico).
Tengo que almacenar UTC dateTime en DB.
He convertido el dateTime dado en la zona horaria específica a UTC. para eso seguí el código a continuación.
Mi entrada dateTime es "20121225 10:00:00 Z" zona horaria es "Asia / Calcuta"
Mi servidor / base de datos (oráculo) se está ejecutando en la misma zona horaria (IST) "Asia / Calcuta"
Obtener 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 (oráculo) ha almacenado el mismo dateTime: "20121225 10:00:00
dado dateTime: "20121225 10:00:00
no en UTC.
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 Timestamp tiene impacto en la zona horaria mientras se almacena en DB. ¿Qué estoy haciendo mal aquí?
Una pregunta más:
¿ timeStamp.toString()
en la zona horaria local como java.util.date
? ¿No es UTC?
Creo que la respuesta correcta debería ser java.sql.Timestamp NO es específica de la zona horaria. Timestamp es un compuesto de java.util.Date y un valor de nanosegundos por separado. No hay información de zona horaria en esta clase. Por lo tanto, 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) Calendar es utilizado por el controlador para cambiar la zona horaria predeterminada. Pero Timestamp todavía tiene milisegundos en GMT.
La API no está clara sobre cómo exactamente se supone que el controlador JDBC debe usar Calendar. 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.
Es específico de tu controlador. Debe proporcionar un parámetro en su programa Java para indicarle la zona horaria que desea usar.
java -Duser.timezone="America/New_York" GetCurrentDateTimeZone
Más 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 here
Para Mysql, tenemos una limitación. En el controlador Mysql doc , tenemos:
Los siguientes son algunos problemas y limitaciones conocidos para MySQL Connector / J: cuando Connector / J recupera las marcas de tiempo para un horario de verano (DST) utilizando el método getTimeStamp () en el conjunto de resultados, algunos de los valores devueltos pueden ser incorrectos. Los errores pueden evitarse utilizando las siguientes opciones de conexión cuando se conecta a una base de datos:
useTimezone=true
useLegacyDatetimeCode=false
serverTimezone=UTC
Entonces, cuando no usamos estos parámetros y llamamos a setTimestamp or getTimestamp
con calendario o sin calendario, tenemos la marca de tiempo en la zona horaria jvm.
Ejemplo:
La zona horaria 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 regresa: 1461100256000 = 19/04/2016 - 21:10:56 GMT
El segundo método regresa: 1461100256000 = 19/04/2016 - 21:10:56 GMT
El tercer método regresa: 1461085856000 = 19/04/2016 - 17:10:56 GMT
En lugar de Oracle, cuando utilizamos las mismas llamadas, tenemos:
El primer método devuelve: 1461093056000 = 19/04/2016 - 19:10:56 GMT
El segundo método regresa: 1461100256000 = 19/04/2016 - 21:10:56 GMT
El tercer método regresa: 1461085856000 = 19/04/2016 - 17:10:56 GMT
NB: No es necesario especificar los parámetros para Oracle.