java - util - simpledateformat timezone




Warum ergibt das Abziehen dieser beiden Male(1927) ein seltsames Ergebnis? (6)

Wenn ich das folgende Programm ausführen, werden zwei Datumsfolgen im Abstand von 1 Sekunde analysiert und miteinander verglichen:

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

Die Ausgabe ist:

353

Warum ist ld4-ld3 nicht 1 (wie ich es von dem Zeitunterschied von einer Sekunde erwarten würde), sondern 353 ?

Wenn ich die Datumsangaben in Sekunden später umstelle:

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

Dann wird ld4-ld3 1 .

Java-Version:

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

Anstatt jedes Datum zu konvertieren, verwenden Sie den folgenden Code

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

Und sehen Sie das Ergebnis ist:

1

Die Moral dieser Fremdheit ist:

  • Verwenden Sie möglichst Datums- und Uhrzeitangaben in UTC.
  • Wenn Sie in UTC kein Datum oder keine Uhrzeit anzeigen können, geben Sie immer die Zeitzone an.
  • Wenn Sie in UTC kein Datum und keine Uhrzeit eingeben können, benötigen Sie eine explizit angegebene Zeitzone.

Es tut mir leid, das sagen zu müssen, aber die Diskontinuität hat sich etwas verschoben

JDK 6 vor zwei Jahren und in JDK 7 vor kurzem in Update 25 .

Lektion zu lernen: Vermeiden Sie unter allen Umständen Nicht-UTC-Zeiten, mit Ausnahme der Anzeige.


IMHO die durchdringende, implizite Lokalisierung in Java ist der größte Konstruktionsfehler. Es ist zwar für Benutzeroberflächen gedacht, aber ehrlich gesagt, wer Java heute wirklich für Benutzeroberflächen verwendet, mit Ausnahme einiger IDEs, bei denen Sie die Lokalisierung grundsätzlich ignorieren können, weil Programmierer nicht genau die Zielgruppe dafür sind. Sie können das Problem (insbesondere auf Linux-Servern) beheben, indem Sie

  • export LC_ALL = C TZ = UTC
  • Stellen Sie Ihre Systemuhr auf UTC
  • Verwenden Sie niemals lokalisierte Implementierungen, es sei denn, dies ist unbedingt erforderlich (dh nur zur Anzeige).

Den Mitgliedern des Java Community Process empfehle ich:

  • machen lokalisierte Methoden nicht zum Standard, sondern verlangen, dass der Benutzer die Lokalisierung explizit anfordert.
  • Verwenden Sie stattdessen UTF-8 / UTC als FIXED- Standard, da dies heute einfach der Standard ist. Es gibt keinen Grund, etwas anderes zu tun, es sei denn, Sie möchten solche Threads erstellen.

Ich meine, komm schon, sind globale statische Variablen kein Anti-OO-Muster? Nichts anderes sind diese durchdringenden Standardeinstellungen, die durch einige rudimentäre Umgebungsvariablen vorgegeben werden.


Wenn Sie die Zeit inkrementieren, sollten Sie wieder in UTC konvertieren und dann addieren oder subtrahieren. Verwenden Sie nur die Ortszeit für die Anzeige.

Auf diese Weise können Sie alle Zeiträume durchlaufen, in denen Stunden oder Minuten zweimal vorkommen.

Wenn Sie in UTC konvertiert haben, fügen Sie jede Sekunde hinzu und konvertieren Sie sie in lokale Zeit für die Anzeige. Sie würden durch 11:54:08 Uhr LMT - 23:59:59 Uhr LMT und dann um 11:54:08 Uhr CST - 23:59:59 Uhr CST gehen.


Wie von anderen erklärt, gibt es dort eine Diskontinuität. Es gibt zwei mögliche Zeitzonenversätze für 1927-12-31 23:54:08 in Asia/Shanghai , aber nur einen Versatz für 1927-12-31 23:54:07 . Abhängig davon, welcher Versatz verwendet wird, gibt es entweder eine Differenz von 1 Sekunde oder 5 Minuten und 53 Sekunden.

Diese geringfügige Verschiebung der Offsets anstelle der üblichen einstündigen Sommerzeit (Sommerzeit), die wir gewohnt sind, verdeckt das Problem ein wenig.

Beachten Sie, dass das 2013a-Update der Zeitzonen-Datenbank diese Diskontinuität einige Sekunden früher verschoben hat, der Effekt jedoch noch zu beobachten ist.

Das neue java.time Paket auf Java 8 macht es klarer zu erkennen und stellt Tools für den Umgang damit bereit. Gegeben:

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

Dann ist durationAtEarlierOffset eine Sekunde, durationAtLaterOffset fünf Minuten und 53 Sekunden.

Auch diese beiden Offsets sind gleich:

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

Diese beiden unterscheiden sich jedoch:

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

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

Sie können dasselbe Problem beim Vergleich von 1927-12-31 23:59:59 mit 1928-01-01 00:00:00 . In diesem Fall ist es jedoch der frühere Offset, der die längere Divergenz erzeugt früheres Datum mit zwei möglichen Offsets.

Eine andere Möglichkeit, dies zu erreichen, besteht darin, zu prüfen, ob gerade ein Übergang stattfindet. Wir können das so machen:

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

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

Sie können überprüfen, ob es sich bei dem Übergang um eine Überlappung handelt. In diesem Fall gibt es mehr als einen gültigen Versatz für dieses Datum / diese Uhrzeit oder eine Lücke. In diesem Fall ist das Datum / die Uhrzeit für diese Zonen-ID nicht gültig. Verwenden Sie dazu die isOverlap() und isGap() -Methoden auf zot4 .

Ich hoffe, das hilft den Leuten, mit dieser Art von Problem umzugehen, sobald Java 8 verfügbar ist oder für diejenigen, die Java 7 verwenden, die den JSR 310-Backport verwenden.





timezone