Rozwiązaniem jest ustawienie parametru połączenia JDBC noDatetimeStringSync=true
z useLegacyDatetimeCode=false
. Jako bonus znalazłem również sessionVariables=time_zone='-00:00'
łagodzi potrzebę set time_zone
wyraźnie przy każdym nowym połączeniu.
Istnieje pewien "inteligentny" kod konwersji strefy czasowej, który jest aktywowany głęboko w ResultSet.getString()
metoda, gdy wykryje, że kolumna jest TIMESTAMP
kolumna.
Niestety, ten inteligentny kod ma błąd:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
zwraca Timestamp
błędnie otagowane do domyślnej strefy czasowej JVM, nawet jeśli tz
parametr jest ustawiony na coś innego:
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;
}
Zwracany ts
byłby całkowicie prawidłowy, z wyjątkiem sytuacji, gdy dalej w łańcuchu wywołań jest konwertowany z powrotem na ciąg znaków za pomocą samego toString()
metoda, która renderuje ts
jako ciąg reprezentujący to, co zegar wyświetliłby w domyślnej strefie czasowej JVM, zamiast ciągu reprezentującego czas w UTC. W 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();
Ustawienie noDatetimeStringSync=true
wyłącza cały bałagan parsowania/odtwarzania i po prostu zwraca wartość ciągu w postaci otrzymanej z bazy danych.
Wyjście testowe:
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
jest nadal ważne, ponieważ zmienia zachowanie getDefaultTimeZone()
aby użyć TZ serwera bazy danych.
Szukając tego, znalazłem również dokumentację dla useJDBCCompliantTimezoneShift
jest niepoprawne, chociaż nie ma znaczenia:dokumentacja mówi [Jest to część starszego kodu daty i godziny, dlatego właściwość ma wpływ tylko wtedy, gdy „useLegacyDatetimeCode=true”. ], ale to źle, zobacz ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean)
.