Después de investigar con especialistas en el tema, programadores de deferentes áreas y maestros hemos dado con la solución al problema y la plasmamos en este post.
Solución:
Aunque no se especifica explícitamente para setTimestamp(int parameterIndex, Timestamp x)
Los conductores tienen que seguir las reglas establecidas por el setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
javadoc:
Establece el parámetro designado en el dado
java.sql.Timestamp
valor, usando el dadoCalendar
objeto. El conductor utiliza elCalendar
objeto para construir un SQLTIMESTAMP
valor, que el controlador luego envía a la base de datos. Con unCalendar
objeto, el conductor puede calcular la marca de tiempo teniendo en cuenta una zona horaria personalizada. Si noCalendar
se especifica el objeto, el controlador utiliza la zona horaria predeterminada, que es la de la máquina virtual que ejecuta la aplicación.
Cuando llamas con setTimestamp(int parameterIndex, Timestamp x)
el controlador JDBC usa 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 se almacena en la base de datos, y si la columna de la base de datos no almacena información de la zona horaria, entonces se perderá cualquier información sobre la zona (lo que significa que depende de la(s) aplicación(es) que usan la base de datos para usar 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. 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 recupera nuevamente: lo recupera nuevamente como “2012-12-25 10:00:00 UTC” (pero solo si lo recupera usando getTimestamp(..)
), pero cuando otra aplicación acceda a la base de datos en la zona horaria 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 el setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
con una instancia de Calendario en la zona horaria requerida. Solo asegúrese de usar el getter equivalente con la misma zona horaria al recuperar valores (si usa un TIMESTAMP
sin información de zona horaria en su base de datos).
Entonces, suponiendo 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 debe admitir java.time.LocalDateTime
(y java.time.LocalTime
) por TIMESTAMP
(y TIME
) a través de get/set/updateObject
. los java.time.Local*
las 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).
Creo que la respuesta correcta debería ser java.sql.Timestamp NO es específico de la zona horaria. La marca de tiempo es un compuesto de java.util.Date y un valor de nanosegundos separado. No hay información de zona horaria en esta clase. Por lo tanto, al igual que Fecha, 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 parámetroIndex, Timestamp x, Calendar cal), el controlador utiliza Calendar para cambiar la zona horaria predeterminada. Pero la marca de tiempo todavía tiene milisegundos en GMT.
La API no tiene claro cómo se supone exactamente que el controlador JDBC debe usar Calendar. Los proveedores parecen sentirse libres de interpretarlo, por ejemplo, la última vez que trabajé con MySQL 5.5 Calendar, el controlador simplemente ignoró Calendar tanto en PreparedStatement.setTimestamp como en ResultSet.getTimestamp.
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 marcas de tiempo para un día de cambio de horario de verano (DST) usando el método getTimeStamp() en el conjunto de resultados, algunos de los valores devueltos pueden ser incorrectos. Los errores se pueden evitar utilizando las siguientes opciones de conexión al conectarse a una base de datos:
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
Nota:
No es necesario especificar los parámetros para Oracle.
valoraciones y comentarios
Eres capaz de auxiliar nuestra ocupación ejecutando un comentario o dejando una valoración te lo agradecemos.