with - timezone in java example




Por que subtrair essas duas vezes(em 1927) dando um resultado estranho? (6)

Se eu executar o programa a seguir, que analisa duas strings de data e tempos de referência separados por 1 segundo e as compara:

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

A saída é:

353

Por que ld4-ld3 não é 1 (como seria de esperar da diferença de um segundo nos tempos), mas sim 353 ?

Se eu mudar as datas para tempos 1 segundo depois:

String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

Então ld4-ld3 será 1 .

Versão de Java:

java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)

Timezone(`TimeZone.getDefault()`):

sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN

É uma mudança de fuso horário no dia 31 de dezembro em Xangai.

Veja esta página para detalhes de 1927 em Xangai. Basicamente à meia-noite do final de 1927, os relógios voltaram 5 minutos e 52 segundos. Então "1927-12-31 23:54:08" realmente aconteceu duas vezes, e parece que o Java está analisando como o último instante possível para aquela data / hora local - daí a diferença.

Apenas mais um episódio no mundo muitas vezes estranho e maravilhoso de fusos horários.

EDIT: Pare de pressionar! A história muda ...

A questão original não demonstraria mais o mesmo comportamento, se reconstruída com a versão 2013a do TZDB . Em 2013a, o resultado seria de 358 segundos, com um tempo de transição de 23:54:03 em vez de 23:54:08.

Eu só notei isso porque estou coletando perguntas como essa na Noda Time, na forma de TZDB ... O teste foi alterado agora, mas isso apenas mostra - nem mesmo os dados históricos são seguros.

EDIT: História mudou de novo ...

Em TZDB 2014f, o tempo da mudança mudou para 1900-12-31, e agora é apenas uma mudança de 343 segundos (então o tempo entre t+1 é de 344 segundos, se você entender o que quero dizer).

EDIT: Para responder a uma pergunta em torno de uma transição em 1900 ... parece que a implementação de fuso horário Java trata todos os fusos horários como simplesmente estar em seu tempo padrão para qualquer momento antes do início de 1900 UTC:

import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

O código acima não produz saída na minha máquina Windows. Portanto, qualquer fuso horário que tenha algum offset diferente do padrão no início de 1900 contará isso como uma transição. O próprio TZDB tem alguns dados anteriores a ele e não depende de nenhuma idéia de um tempo padrão "fixo" (que é o que getRawOffset supõe ser um conceito válido), portanto outras bibliotecas não precisam introduzir essa transição artificial.


A moral dessa estranheza é:

  • Use datas e horas em UTC sempre que possível.
  • Se você não puder exibir uma data ou hora no UTC, sempre indique o fuso horário.
  • Se você não puder exigir uma data / hora de entrada no UTC, solicite um fuso horário explicitamente indicado.

Conforme explicado por outros, há uma descontinuidade no tempo lá. Há duas possíveis compensações de fusos horários para 1927-12-31 23:54:08 na Asia/Shanghai , mas apenas um deslocamento para 1927-12-31 23:54:07 . Então, dependendo de qual offset é usado, há uma diferença de um segundo ou uma diferença de 5 minutos e 53 segundos.

Essa ligeira mudança de compensações, em vez da habitual economia de horário de verão (horário de verão) a que estamos acostumados, obscurece um pouco o problema.

Observe que a atualização 2013a do banco de dados de fuso horário moveu essa descontinuidade alguns segundos antes, mas o efeito ainda seria observável.

O novo pacote java.time no Java 8 permite ver isso com mais clareza e fornecer ferramentas para lidar com isso. Dado:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

String str3 = "1927-12-31 23:54:07";  
String str4 = "1927-12-31 23:54:08";  

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

Então durationAtEarlierOffset será um segundo, enquanto durationAtLaterOffset será cinco minutos e 53 segundos.

Além disso, esses dois deslocamentos são os mesmos:

// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

Mas estes dois são diferentes:

// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

Você pode ver o mesmo problema comparando 1927-12-31 23:59:59 com 1928-01-01 00:00:00 , no entanto, neste caso, é a compensação anterior que produz a divergência mais longa, e é a data anterior que tenha dois possíveis desvios.

Outra maneira de abordar isso é verificar se há uma transição acontecendo. Nós podemos fazer isso assim:

// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

Você pode verificar se a transição é uma sobreposição - nesse caso, há mais de um deslocamento válido para essa data / hora - ou um intervalo - nesse caso, essa data / hora não é válida para esse id de zona - usando o isOverlap() e métodos isGap() no zot4 .

Espero que isso ajude as pessoas a lidar com esse tipo de problema quando o Java 8 se tornar amplamente disponível, ou para aqueles que usam o Java 7 que adotam o backport do JSR 310.


Em vez de converter cada data, você usa o seguinte código

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

E veja o resultado é:

1

Lamento dizer isso, mas a descontinuidade do tempo mudou um pouco

JDK 6 há dois anos, e no JDK 7 recentemente na atualização 25 .

Lição para aprender: evite tempos não UTC a todo custo, exceto, talvez, para exibição.


Você encontrou uma descontinuidade no horário local :

Quando o horário padrão local estava prestes a chegar ao domingo, 1. de janeiro de 1928, 00:00:00 os relógios foram invertidos 0:05:52 horas para sábado, 31. dezembro 1927, 23:54:08 hora padrão local

Isso não é particularmente estranho e aconteceu praticamente em todos os lugares em um momento ou outro, como fusos horários foram trocados ou alterados devido a ações políticas ou administrativas.





timezone