Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Quais são as configurações do driver JDBC-mysql para manipulação sã de DATETIME e TIMESTAMP em UTC?


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) .