La solution consiste à définir le paramètre de connexion JDBC noDatetimeStringSync=true
avec useLegacyDatetimeCode=false
. En bonus j'ai aussi trouvé sessionVariables=time_zone='-00:00'
atténue le besoin de set time_zone
explicitement à chaque nouvelle connexion.
Il existe un code de conversion de fuseau horaire "intelligent" qui est activé au plus profond de ResultSet.getString()
lorsqu'elle détecte que la colonne est un TIMESTAMP
colonne.
Hélas, ce code intelligent a un bogue :TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
renvoie un Timestamp
mal étiqueté au fuseau horaire par défaut de la JVM, même lorsque le tz
paramètre est défini sur autre chose :
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;
}
Le retour ts
serait parfaitement valide sauf lorsque plus haut dans la chaîne d'appel, il est reconverti en une chaîne à l'aide du simple toString()
méthode, qui rend le ts
sous forme de chaîne représentant ce qu'une horloge afficherait dans le fuseau horaire par défaut de la JVM, au lieu d'une représentation sous forme de chaîne de l'heure en UTC. Dans 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();
Définition de noDatetimeStringSync=true
désactive tout le gâchis d'analyse/désanalyse et renvoie simplement la valeur de chaîne telle qu'elle est reçue de la base de données.
Résultat du test :
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Le useLegacyDatetimeCode=false
est toujours important car il modifie le comportement de getDefaultTimeZone()
pour utiliser le TZ du serveur de base de données.
En poursuivant, j'ai également trouvé la documentation pour useJDBCCompliantTimezoneShift
est incorrect, bien que cela ne fasse aucune différence :la documentation indique [Cela fait partie du code date-heure hérité, donc la propriété n'a d'effet que lorsque "useLegacyDatetimeCode=true." ], mais c'est faux, voir ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean)
.