De oplossing is om de JDBC-verbindingsparameter noDatetimeStringSync=true
in te stellen met useLegacyDatetimeCode=false
. Als bonus vond ik ook sessionVariables=time_zone='-00:00'
verlicht de noodzaak om set time_zone
expliciet bij elke nieuwe verbinding.
Er is een "intelligente" tijdzone-conversiecode die diep in de ResultSet.getString()
wordt geactiveerd methode wanneer het detecteert dat de kolom een TIMESTAMP
. is kolom.
Helaas, deze intelligente code heeft een fout:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
retourneert een Timestamp
verkeerd getagd naar de standaard tijdzone van de JVM, zelfs wanneer de tz
parameter is ingesteld op iets anders:
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;
}
De terugkeer ts
zou perfect geldig zijn, behalve wanneer hoger in de oproepketen, het terug wordt geconverteerd naar een string met behulp van de kale toString()
methode, die de ts
. weergeeft als een tekenreeks die aangeeft wat een klok zou weergeven in de JVM-standaardtijdzone, in plaats van een tekenreeksrepresentatie van de tijd 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();
noDatetimeStringSync=true
instellen schakelt de gehele ontleden/ontleden puinhoop uit en retourneert gewoon de tekenreekswaarde zoals deze is ontvangen van de database.
Testuitgang:
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
De useLegacyDatetimeCode=false
is nog steeds belangrijk omdat het het gedrag van getDefaultTimeZone()
. verandert om de TZ van de databaseserver te gebruiken.
Terwijl ik dit najaagde, vond ik ook de documentatie voor useJDBCCompliantTimezoneShift
is onjuist, hoewel het geen verschil maakt:documentatie zegt [Dit maakt deel uit van de oude datum-tijdcode, dus de eigenschap heeft alleen effect als "useLegacyDatetimeCode=true." ], maar dat is fout, zie ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean)
.