Решението е да зададете параметър за JDBC връзка noDatetimeStringSync=true
с useLegacyDatetimeCode=false
. Като бонус намерих също sessionVariables=time_zone='-00:00'
облекчава необходимостта от set time_zone
изрично при всяка нова връзка.
Има някакъв "интелигентен" код за преобразуване на часовата зона, който се активира дълбоко в ResultSet.getString()
метод, когато открие, че колоната е TIMESTAMP
колона.
Уви, този интелигентен код има грешка:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
връща Timestamp
грешно маркиран към часовата зона по подразбиране на JVM, дори когато tz
параметърът е настроен на нещо друго:
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;
}
Връщането ts
би бил напълно валиден, освен когато по-нагоре във веригата на повиквания, той се преобразува обратно в низ с помощта на голия toString()
метод, който изобразява ts
като String, представляващ това, което часовникът ще показва в часовата зона по подразбиране на JVM, вместо низово представяне на часа в UTC. В 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();
Настройка noDatetimeStringSync=true
деактивира цялата бъркотия при анализа/разбора и просто връща стойността на низа, както е получена от базата данни.
Тестов изход:
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
все още е важно, защото променя поведението на getDefaultTimeZone()
за да използвате TZ на сървъра на базата данни.
Докато гонех това, намерих и документацията за useJDBCCompliantTimezoneShift
е неправилно, въпреки че няма разлика:в документацията се казва [Това е част от наследения код за дата и час, така че свойството има ефект само когато "useLegacyDatetimeCode=true." ], но това е грешно, вижте ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean)
.