A solução é definir o parâmetro de conexão JDBC
noDatetimeStringSync=true com useLegacyDatetimeCode=false . Como bônus, também encontrei sessionVariables=time_zone='-00:00' alivia a necessidade de set time_zone explicitamente em cada nova conexão. Há algum código de conversão de fuso horário "inteligente" que é ativado profundamente dentro do
ResultSet.getString() quando detecta que a coluna é um TIMESTAMP coluna. Infelizmente, este código inteligente tem um bug:
TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) retorna um Timestamp marcado erroneamente no fuso horário padrão da JVM, mesmo quando o tz parâmetro está definido para outra coisa: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;
}
O retorno
ts seria perfeitamente válido, exceto quando mais acima na cadeia de chamadas, ele é convertido novamente em uma string usando o toString() simples método, que renderiza o ts como uma String representando o que um relógio exibiria no fuso horário padrão da JVM, em vez de uma representação de String da hora em UTC. Em 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();
Configurando
noDatetimeStringSync=true desativa toda a confusão de análise/desparse e apenas retorna o valor da string como é recebido do banco de dados. Saída de teste:
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
O
useLegacyDatetimeCode=false ainda é importante porque altera o comportamento de getDefaultTimeZone() para usar a TZ do servidor de banco de dados. Enquanto procurava isso, também encontrei a documentação para
useJDBCCompliantTimezoneShift está incorreto, embora não faça diferença:a documentação diz [Isso faz parte do código de data e hora herdado, portanto, a propriedade tem efeito apenas quando "useLegacyDatetimeCode=true." ], mas isso está errado, veja ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean) .