sql >> Databáze >  >> RDS >> Mysql

Jaká jsou nastavení ovladače JDBC-mysql pro rozumné zacházení s DATETIME a TIMESTAMP v UTC?

Řešením je nastavit parametr připojení JDBC noDatetimeStringSync=true s useLegacyDatetimeCode=false . Jako bonus jsem také našel sessionVariables=time_zone='-00:00' zmírňuje potřebu set time_zone explicitně při každém novém připojení.

Existuje nějaký „inteligentní“ konverzní kód časového pásma, který se aktivuje hluboko uvnitř ResultSet.getString() metoda, když zjistí, že sloupec je TIMESTAMP sloupec.

Bohužel, tento inteligentní kód má chybu:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) vrátí Timestamp nesprávně označený na výchozí časové pásmo JVM, i když tz parametr je nastaven na něco jiného:

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

Návrat ts by bylo naprosto platné, s výjimkou případů, kdy je dále v řetězci volání převedeno zpět na řetězec pomocí holého toString() metoda, která vykreslí ts jako String reprezentující to, co by hodiny zobrazily ve výchozím časovém pásmu JVM, namísto String reprezentace času v UTC. V 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();

Nastavení noDatetimeStringSync=true deaktivuje celou změť analýzy/rozboru a pouze vrátí hodnotu řetězce tak, jak je přijata z databáze.

Testovací výstup:

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

useLegacyDatetimeCode=false je stále důležité, protože mění chování getDefaultTimeZone() použít TZ databázového serveru.

Při hledání toho jsem také našel dokumentaci pro useJDBCCompliantTimezoneShift je nesprávná, i když v tom není žádný rozdíl:dokumentace říká [Toto je část staršího kódu datum-čas, takže vlastnost má účinek pouze tehdy, když "useLegacyDatetimeCode=true." ], ale to je špatně, viz ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean) .




  1. Jednoduchá parametrizace a triviální plány — 1. část

  2. Jak zajistit, aby vaše přístupové databáze byly ultra rychlé!

  3. Databáze + ověřování systému Windows + uživatelské jméno/heslo?

  4. MySQL:Přidejte omezení, pokud neexistuje