sql >> Base de Datos >  >> RDS >> Mysql

¿Cuáles son las configuraciones del controlador JDBC-mysql para un manejo adecuado de DATETIME y TIMESTAMP en UTC?

La solución es establecer el parámetro de conexión JDBC noDatetimeStringSync=true con useLegacyDatetimeCode=false . Como beneficio adicional, también encontré sessionVariables=time_zone='-00:00' alivia la necesidad de set time_zone explícitamente en cada nueva conexión.

Hay un código de conversión de zona horaria "inteligente" que se activa en lo profundo de ResultSet.getString() método cuando detecta que la columna es un TIMESTAMP columna.

Por desgracia, este código inteligente tiene un error:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) devuelve una Timestamp mal etiquetado en la zona horaria predeterminada de la JVM, incluso cuando tz el parámetro está establecido en otra cosa:

final static Timestamp fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) {
    Calendar cal = (tz == null) ? new GregorianCalendar() : new GregorianCalendar(tz);
    cal.clear();

    // why-oh-why is this different than java.util.date, in the year part, but it still keeps the silly '0' for the start month????
    cal.set(year, month - 1, day, hour, minute, seconds);

    long tsAsMillis = cal.getTimeInMillis();

    Timestamp ts = new Timestamp(tsAsMillis);
    ts.setNanos(secondsPart);

    return ts;
}

El retorno ts sería perfectamente válido excepto cuando más arriba en la cadena de llamadas, se convierte de nuevo en una cadena usando el toString() desnudo método, que representa el ts como una cadena que representa lo que mostraría un reloj en la zona horaria predeterminada de JVM, en lugar de una representación de cadena de la hora en UTC. En ResultSetImpl.getStringInternal(int columnIndex, boolean checkDateTypes) :

                case Types.TIMESTAMP:
                    Timestamp ts = getTimestampFromString(columnIndex, null, stringVal, this.getDefaultTimeZone(), false);

                    if (ts == null) {
                        this.wasNullFlag = true;

                        return null;
                    }

                    this.wasNullFlag = false;

                    return ts.toString();

Configuración de noDatetimeStringSync=true deshabilita todo el lío de analizar/desanalizar y simplemente devuelve el valor de la cadena tal como se recibe de la base de datos.

Salida de prueba:

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

El useLegacyDatetimeCode=false sigue siendo importante porque cambia el comportamiento de getDefaultTimeZone() para utilizar la TZ del servidor de la base de datos.

Mientras perseguía esto, también encontré la documentación para useJDBCCompliantTimezoneShift es incorrecto, aunque no hace ninguna diferencia:la documentación dice [Esto es parte del código de fecha y hora heredado, por lo tanto, la propiedad solo tiene efecto cuando "useLegacyDatetimeCode=true". ], pero eso es incorrecto, consulte ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean) .