Ř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)
.