Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Was sind JDBC-mysql-Treibereinstellungen für eine vernünftige Handhabung von DATETIME und TIMESTAMP in UTC?

Die Lösung besteht darin, den JDBC-Verbindungsparameter noDatetimeStringSync=true zu setzen mit useLegacyDatetimeCode=false . Als Bonus habe ich auch sessionVariables=time_zone='-00:00' gefunden verringert die Notwendigkeit, set time_zone explizit bei jeder neuen Verbindung.

Es gibt einen "intelligenten" Zeitzonen-Umwandlungscode, der tief in ResultSet.getString() aktiviert wird -Methode, wenn erkannt wird, dass die Spalte ein TIMESTAMP ist Spalte.

Leider hat dieser intelligente Code einen Fehler:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) gibt einen Timestamp zurück falsch in die Standardzeitzone der JVM getaggt, selbst wenn der tz Parameter auf etwas anderes gesetzt ist:

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

Die Rückgabe ts wäre vollkommen gültig, außer wenn es weiter oben in der Aufrufkette mit dem bloßen toString() zurück in einen String konvertiert wird -Methode, die das ts rendert als String, der darstellt, was eine Uhr in der JVM-Standardzeitzone anzeigen würde, anstelle einer String-Darstellung der Zeit 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();

Setzen von noDatetimeStringSync=true deaktiviert das gesamte Parsing/Unparse-Durcheinander und gibt nur den String-Wert zurück, wie er von der Datenbank empfangen wird.

Testausgabe:

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

Der useLegacyDatetimeCode=false ist immer noch wichtig, da es das Verhalten von getDefaultTimeZone() ändert um die TZ des Datenbankservers zu verwenden.

Während ich dem nachjagte, fand ich auch die Dokumentation für useJDBCCompliantTimezoneShift ist falsch, obwohl es keinen Unterschied macht:Dokumentation sagt [Dies ist Teil des veralteten Datums-Uhrzeit-Codes, daher hat die Eigenschaft nur eine Auswirkung, wenn "useLegacyDatetimeCode=true." ], aber das ist falsch, siehe ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean) .