Mysql
 sql >> Database >  >> RDS >> Mysql

Quali sono le impostazioni del driver JDBC-mysql per una corretta gestione di DATETIME e TIMESTAMP in UTC?

La soluzione è impostare il parametro di connessione JDBC noDatetimeStringSync=true con useLegacyDatetimeCode=false . Come bonus ho trovato anche sessionVariables=time_zone='-00:00' allevia la necessità di set time_zone esplicitamente ad ogni nuova connessione.

Esiste un codice di conversione del fuso orario "intelligente" che viene attivato all'interno di ResultSet.getString() metodo quando rileva che la colonna è un TIMESTAMP colonna.

Purtroppo, questo codice intelligente ha un bug:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) restituisce un Timestamp erroneamente contrassegnato con il fuso orario predefinito della JVM, anche quando tz parametro è impostato su qualcos'altro:

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;
}

Il ritorno ts sarebbe perfettamente valido tranne quando più in alto nella catena di chiamate, viene riconvertito in una stringa usando il semplice toString() metodo, che rende il ts come una stringa che rappresenta ciò che un orologio visualizzerebbe nel fuso orario predefinito della JVM, invece di una rappresentazione String dell'ora in UTC. In 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();

Impostazione di noDatetimeStringSync=true disabilita l'intero parse/unparse mess e restituisce solo il valore della stringa così come viene ricevuto dal database.

Uscita di prova:

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

Il useLegacyDatetimeCode=false è ancora importante perché cambia il comportamento di getDefaultTimeZone() per utilizzare la TZ del server di database.

Durante la ricerca ho anche trovato la documentazione per useJDBCCompliantTimezoneShift non è corretto, anche se non fa alcuna differenza:la documentazione dice [Questo fa parte del codice data-ora legacy, quindi la proprietà ha effetto solo quando "useLegacyDatetimeCode=true." ], ma è sbagliato, vedi ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean) .