Pourquoi est soustrayant ces deux fois (en 1927), donnant un résultat étrange?

si je lance le programme suivant, qui analyse deux chaînes de datation se référant à des temps 1 seconde l'une de l'autre et les compare:

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);
}

la sortie est:

353

pourquoi ld4-ld3 n'est pas 1 (comme je m'y attendais avec la différence d'une seconde dans les temps), mais 353 ?

si je change les dates en heures 1 seconde plus tard:

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

puis ld4-ld3 sera 1 .


version 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
6122
demandé sur B. Desai 2011-07-27 12:15:58
la source

8 ответов

changement de fuseau horaire le 31 décembre à Shanghai.

Voir cette page pour les détails de 1927 à Shanghai. Fondamentalement, à minuit, à la fin de 1927, les horloges revint 5 minutes et 52 secondes. Donc "1927-12-31 23:54: 08" s'est réellement produit deux fois, et il semble que Java l'analyse comme le plus tard instant possible pour cette date/heure locale - d'où la différence.

juste un autre épisode dans le monde souvent étrange et merveilleux des fuseaux horaires.

EDIT: Arrêter de presse de la! L'histoire change...

la question initiale ne démontrerait plus tout à fait le même comportement, si elle était reconstruite avec la version 2013a de TZDB . Dans 2013a, le résultat serait 358 secondes, avec un temps de transition de 23:54:03 au lieu de 23:54:08.

j'ai remarqué seulement parce que je collectionne des questions comme ceci en aucun temps, sous la forme de essais unitaires ... Le test a maintenant été modifié, mais il va tout simplement montrer - même les données historiques ne sont pas sûres.

EDIT: l'Histoire a changé de nouveau...

dans TZDB 2014f, l'heure du changement est passée à 1900-12-31, et c'est maintenant un simple changement de 343 secondes (donc le temps entre t et t+1 est de 344 secondes, si vous voyez ce que je veux dire).

EDIT: Pour répondre à une question autour d'une transition à 1900... il semble que L'implémentation du fuseau horaire Java traite tous les fuseaux horaires comme étant simplement dans leur temps standard pour n'importe quel instant avant le début 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);
            }
        }
    }
}

le code ci-dessus ne produit aucune sortie sur ma machine Windows. Ainsi, tout fuseau horaire qui a un décalage autre que son décalage standard au début de 1900 comptera cela comme une transition. TZDB lui-même a quelques données remontant plus tôt que cela, et ne s'appuie sur aucune idée d'un temps standard "fixe" (ce qui est ce que getRawOffset suppose être un concept valide) de sorte que les autres bibliothèques n'ont pas besoin d'introduire cette transition artificielle.

9862
répondu Jon Skeet 2017-08-24 17:51:42
la source

Vous avez rencontré un heure locale discontinuité :

lorsque l'heure normale locale était sur le point d'atteindre le dimanche, 1. En janvier 1928, 00:00:00 Les horloges ont été tournées en arrière de 0: 05: 52 à samedi, 31. En décembre 1927, 23:54:08, heure normale locale au lieu de

ce n'est pas particulièrement étrange et s'est produit à peu près partout à un moment ou à un autre car les fuseaux horaires ont été commutés ou changés pour des raisons politiques ou mesures administratives.

1485
répondu Michael Borgwardt 2011-07-27 12:38:51
la source

la morale de cette étrangeté est:

  • dans la mesure du possible, utiliser les dates et les heures en TUC.
  • si vous ne pouvez pas afficher une date ou une heure en TUC, indiquez toujours le fuseau horaire.
  • si vous ne pouvez pas Demander une date/heure d'entrée en TUC, demandez un fuseau horaire explicitement indiqué.
593
répondu Raedwald 2011-07-28 15:50:08
la source

lorsque vous incrémentez le temps, vous devriez convertir de nouveau en UTC et puis Ajouter ou soustraire. Utilisez l'heure locale seulement pour l'affichage.

de cette façon, vous serez en mesure de marcher à travers toutes les périodes où les heures ou les minutes se produisent deux fois.

si vous avez converti en UTC, ajoutez chaque seconde, et convertissez en heure locale pour l'affichage. Vous passeriez par 11: 54:08 p. m. LMT - 11:59: 59 P. M. LMT et puis 11: 54: 08 p. m. CST - 11 h 59 h 59 h (HNE)

327
répondu PatrickO 2011-12-28 00:04:04
la source

au lieu de convertir chaque date, vous utilisez le code suivant

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

et voir le résultat est:

1
272
répondu Rajshri 2018-04-02 14:47:10
la source

je suis désolé de dire cela, mais la discontinuité temporelle a bougé un peu dans

JDK 6 il y a deux ans, et dans JDK 7 tout récemment dans update 25 .

leçon à apprendre: éviter les heures non-UTC à tout prix, sauf, peut-être, pour l'affichage.

193
répondu user1050755 2014-10-31 08:30:57
la source

comme expliqué par d'autres, il y a une discontinuité temporelle. Il y a deux décalages possibles du fuseau horaire pour 1927-12-31 23:54:08 à Asia/Shanghai , mais un seul pour 1927-12-31 23:54:07 . Ainsi, selon l'offset utilisé, il y a soit une différence d'une seconde, soit une différence de 5 minutes et 53 secondes.

ce léger décalage de décalages, au lieu de l'habituelle heure avancée (heure d'été) à laquelle nous sommes habitués, obscurcit un peu le problème.

notez que la mise à jour 2013a de la base de données timezone a déplacé cette discontinuité quelques secondes plus tôt, mais l'effet serait encore observable.

le nouveau paquet java.time sur Java 8 permet une utilisation plus claire, et fournit des outils pour le gérer. Donnée:

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());

puis durationAtEarlierOffset sera d'une seconde, tandis que durationAtLaterOffset sera de cinq minutes et 53 secondes.

aussi, ces deux offsets sont les même chose:

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

mais ces deux - là sont différents:

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

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

vous pouvez voir le même problème en comparant 1927-12-31 23:59:59 avec 1928-01-01 00:00:00 , bien que, dans ce cas, c'est le décalage antérieur qui produit la plus longue divergence, et c'est la date antérieure qui a deux décalages possibles.

une autre façon d'aborder cette question Est de vérifier s'il y a une transition en cours. Nous pouvons faire ceci comme ceci:

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

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

vous pouvez vérifier si la transition est un chevauchement - auquel cas il y a plus d'un décalage valide pour cette date/heure - ou un écart - auquel cas cette date/heure n'est pas valide pour cet id de zone - en utilisant les méthodes isOverlap() et isGap() sur zot4 .

j'espère que cela aidera les gens à gérer ce genre de problème une fois que Java 8 sera largement disponible, ou pour ceux qui utilisent Java 7 qui adoptent le backport JSR 310.

172
répondu Daniel C. Sobral 2014-02-05 22:56:57
la source

IMHO the pervasive, implicite la localisation en Java est son plus grand défaut de conception. Il peut être destiné aux interfaces utilisateur, mais franchement, qui utilise vraiment Java pour les interfaces utilisateur aujourd'hui, sauf pour certains IDEs où vous pouvez essentiellement ignorer la localisation parce que les programmeurs ne sont pas exactement le public cible pour elle. Vous pouvez le corriger (en particulier sur les serveurs Linux) par:

  • exportation LC_ALL=C TZ =UTC
  • ensemble votre horloge système à UTC
  • n'utilisez jamais d'implémentations localisées sauf si c'est absolument nécessaire (c'est-à-dire uniquement pour l'affichage)

À la Java Community Process les membres je vous recommande:

  • ne rend pas les méthodes localisées par défaut, mais exige que l'utilisateur demande explicitement la localisation.
  • utilisez UTF-8 / UTC comme défaut fixe à la place parce que c'est simplement la valeur par défaut aujourd'hui. Il n'y a aucune raison de faire autre chose, sauf si vous voulez produire des fils comme celui-ci.

je veux dire, Allez, les variables statiques globales ne sont-elles pas un modèle anti-OO? Rien d'autre n'est ces valeurs par défaut généralisées données par certaines variables d'environnement rudimentaires.......

142
répondu user1050755 2018-07-24 17:29:48
la source

Autres questions sur java date timezone