Løsningen er at indstille JDBC-forbindelsesparameteren noDatetimeStringSync=true
med useLegacyDatetimeCode=false
. Som en bonus fandt jeg også sessionVariables=time_zone='-00:00'
afhjælper behovet for at set time_zone
eksplicit på hver ny forbindelse.
Der er en "intelligent" tidszonekonverteringskode, der bliver aktiveret dybt inde i ResultSet.getString()
metode, når den registrerer, at kolonnen er en TIMESTAMP
kolonne.
Ak, denne intelligente kode har en fejl:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
returnerer et Timestamp
forkert tagget til JVM'ens standardtidszone, selv når tz
parameter er sat til noget andet:
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;
}
Returneringen ts
ville være helt gyldig, undtagen når længere oppe i opkaldskæden, den konverteres tilbage til en streng ved hjælp af den blottede toString()
metode, som gengiver ts
som en streng, der repræsenterer, hvad et ur ville vise i JVM-standardtidszonen, i stedet for en strengrepræsentation af tiden i UTC. I 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();
Indstilling af noDatetimeStringSync=true
deaktiverer hele parse/unparse messen og returnerer bare strengværdien, som den modtages fra databasen.
Test output:
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
er stadig vigtigt, fordi det ændrer adfærden for getDefaultTimeZone()
for at bruge databaseserverens TZ.
Mens jeg jagtede dette, fandt jeg også dokumentationen for useJDBCCompliantTimezoneShift
er forkert, selvom det ikke gør nogen forskel:dokumentationen siger [Dette er en del af den gamle dato-tidskode, og ejendommen har således kun en effekt, når "useLegacyDatetimeCode=true." ], men det er forkert, se ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean)
.