Warum führt die Subtraktion dieser beiden Epochenmillionen (im Jahr 1927) zu einem seltsamen Ergebnis?
Lesezeit: 8 Minuten
Freier Wind
Wenn ich das folgende Programm ausführe, analysiert es zwei Datumszeichenfolgen, die auf Zeiten im Abstand von 1 Sekunde verweisen, und vergleicht sie:
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-ld3nicht 1 (wie ich es aufgrund des Zeitunterschieds von einer Sekunde erwarten würde), aber 353?
Wenn ich die Daten auf Zeiten 1 Sekunde später ändere:
Die eigentliche Antwort ist, immer, immer Sekunden seit einer Epoche für die Protokollierung zu verwenden, wie die Unix-Epoche, mit 64-Bit-Ganzzahldarstellung (mit Vorzeichen, wenn Sie Stempel vor der Epoche zulassen möchten). Jedes reale Zeitsystem weist ein nichtlineares, nicht monotones Verhalten wie Schaltstunden oder Sommerzeit auf.
Und noch einer vom selben Typ, @ThorbjørnRavnAndersen: youtube.com/watch?v=Uqjg8Kk1HXo (Schaltsekunden). (Dieser stammt von Tom Scotts eigenem YouTube-Kanal, nicht von Computerphile.)
– TRiG
3. Juli 2015 um 16:23
@Phil H „Sekunden seit der Epoche“ (dh Unix-Zeit) ist ebenfalls nichtlinear, in dem Sinne, dass POSIX-Sekunden keine SI-Sekunden sind und in der Länge variieren
– Erinnere dich an Monica
13. September 2020 um 23:57 Uhr
POSIX-Sekunden können in der Länge variieren, aber auch das Stepping ist eine zulässige und nicht ungewöhnliche Implementierungsoption. Das heißt, die Differenz zwischen Zeitstempeln im Abstand von einer Sekunde kann Null sein, wenn eine Schaltsekunde hinzugefügt wird, oder negativ, wenn die Zeitstempel weniger als eine Sekunde voneinander entfernt sind. Die Unix-Zeit verhält sich also nicht monoton wie andere reale Zeitsysteme und ist kein Allheilmittel.
– Erickson
4. Mai 2022 um 20:22 Uhr
Jon Skeet
Am 31. Dezember findet in Shanghai eine Zeitzonenumstellung statt.
Sehen diese Seite für Einzelheiten von 1927 in Shanghai. Grundsätzlich wurden die Uhren Ende 1927 um Mitternacht um 5 Minuten und 52 Sekunden zurückgestellt. „1927-12-31 23:54:08“ ist also tatsächlich zweimal passiert, und es sieht so aus, als würde Java es als analysieren später Möglicher Zeitpunkt für dieses lokale Datum/diese lokale Uhrzeit – daher der Unterschied.
Nur eine weitere Episode in der oft seltsamen und wunderbaren Welt der Zeitzonen.
Bei Neuaufbau mit Version 2013a von TZDB, Die ursprüngliche Frage würde nicht mehr ganz dasselbe Verhalten zeigen. Im Jahr 2013a läge das Ergebnis bei 358 Sekunden, bei einer Übergangszeit von 23:54:03 statt 23:54:08.
Das ist mir nur aufgefallen, weil ich solche Fragen in Noda Time sammle, und zwar in Form von Unit-Tests… Der Test wurde jetzt geändert, aber es zeigt sich: Nicht einmal historische Daten sind sicher.
In TZDB 2014fder Zeitpunkt der Änderung wurde auf den 31.12.1900 verschoben, und die Änderung dauert jetzt nur noch 343 Sekunden (also die Zeit dazwischen). t Und t+1 beträgt 344 Sekunden, wenn Sie verstehen, was ich meine).
Um eine Frage zu einem Übergang um 1900 zu beantworten: Es sieht so aus, als ob die Java-Zeitzonenimplementierung behandelt wird alle Zeitzonen einfach so, dass sie für jeden Zeitpunkt vor Beginn von 1900 UTC in ihrer Standardzeit liegen:
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);
}
}
}
}
Der obige Code erzeugt auf meinem Windows-Rechner keine Ausgabe. Daher gilt jede Zeitzone, die zu Beginn des Jahres 1900 einen anderen als den Standardversatz aufweist, als Übergang. TZDB selbst verfügt über einige Daten, die früher zurückreichen, und verlässt sich nicht auf die Vorstellung einer „festen“ Standardzeit (was ja auch der Fall ist). getRawOffset davon ausgeht, dass es sich um ein gültiges Konzept handelt), sodass andere Bibliotheken diesen künstlichen Übergang nicht einführen müssen.
Als die lokale Standardzeit Sonntag, 1. Januar 1928, 00:00:00 Uhr erreichen sollte, wurden die Uhren um 0:05:52 Stunden auf Samstag, 31. Dezember 1927, 23:54:08 Uhr lokaler Standardzeit zurückgestellt
Dies ist nicht besonders seltsam und ist fast überall zu der einen oder anderen Zeit passiert, da aufgrund politischer oder administrativer Maßnahmen die Zeitzonen umgestellt oder geändert wurden.
Es passiert zweimal im Jahr überall dort, wo Sommerzeit gilt.
– Uckelman
27. November 2020 um 22:57 Uhr
Das hier ist keine Sommerzeit, denke ich. Es ist nur 10 Minuten zurück und nur einmal. Gleichzeitig kann es zweimal im Jahr oder viermal im Jahr (aufgrund des Ramadan) zu Änderungen im Zusammenhang mit der Sommerzeit kommen. oder in einigen Setups sogar einmal im Jahr. Da gibt es keine Regel 🙂
– iwat0qs
27. März 2022 um 10:29
Heutzutage kommt es bei der Sommerzeit im Allgemeinen nicht mehr so vor, da die Zeitumstellung um 1 oder 2 Uhr erfolgt, um sicherzustellen, dass es zu keiner Änderung des Datums kommt und sich die Daten nicht wiederholen.
– Henry Brice
10. April um 17:18 Uhr
Die Moral dieser Seltsamkeit ist:
Verwenden Sie Datums- und Uhrzeitangaben nach Möglichkeit in UTC.
Wenn Sie kein Datum oder keine Uhrzeit in UTC anzeigen können, geben Sie immer die Zeitzone an.
Wenn Sie kein Eingabedatum/Uhrzeit in UTC benötigen, fordern Sie eine explizit angegebene Zeitzone an.
Keiner dieser Punkte würde dieses Ergebnis beeinflussen – es fällt direkt unter den dritten Aufzählungspunkt – und darüber hinaus handelt es sich um einen Zeitpunkt, der mehrere Jahrzehnte vor der Definition von UTC liegt, und kann daher nicht wirklich sinnvoll in UTC ausgedrückt werden.
– Dag Ågren
2. November 2020 um 16:11 Uhr
PatrickO
Wenn Sie die Zeit erhöhen, sollten Sie sie zurück in UTC umrechnen und dann addieren oder subtrahieren. Verwenden Sie ausschließlich die Ortszeit zur Anzeige.
Auf diese Weise können Sie alle Zeiträume durchlaufen, in denen Stunden oder Minuten zweimal vorkommen.
Wenn Sie in UTC konvertiert haben, addieren Sie jede Sekunde und konvertieren Sie sie zur Anzeige in die Ortszeit. Sie würden bis 23:54:08 Uhr gehen LMT – 23:59:59 Uhr LMT und dann 23:54:08 Uhr CST – 23:59:59 Uhr CST.
Rajshri
Anstatt jedes Datum umzuwandeln, können Sie den folgenden Code verwenden:
long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);
Und dann sehen Sie, dass das Ergebnis ist:
1
davnicwil
Es tut mir leid, das sagen zu müssen, aber die Zeitdiskontinuität ist etwas in die Länge gezogen
Lektion zum Lernen: Vermeiden Sie unbedingt Nicht-UTC-Zeiten, außer vielleicht zur Anzeige.
neues QOpenGLWidget
Wie andere erklärt haben, gibt es dort eine Zeitdiskontinuität. Es gibt zwei mögliche Zeitzonen-Offsets für 1927-12-31 23:54:08 bei Asia/Shanghaiaber nur ein Offset für 1927-12-31 23:54:07. Je nachdem, welcher Offset verwendet wird, gibt es also entweder einen Unterschied von einer Sekunde oder einen Unterschied von 5 Minuten und 53 Sekunden.
Diese leichte Verschiebung der Offsets anstelle der üblichen einstündigen Sommerzeit (Sommerzeit), die wir gewohnt sind, verschleiert das Problem ein wenig.
Beachten Sie, dass die Aktualisierung der Zeitzonendatenbank im Jahr 2013 diese Diskontinuität einige Sekunden früher verschoben hat, der Effekt jedoch weiterhin beobachtbar wäre.
Das neue java.time Mit dem Paket auf Java 8 können wir dies klarer erkennen und Tools zur Verfügung stellen, um damit umzugehen. Gegeben:
Beim Vergleich sieht man das gleiche Problem 1927-12-31 23:59:59 mit 1928-01-01 00:00:00allerdings ist es in diesem Fall der frühere Versatz, der die längere Divergenz erzeugt, und es ist das frühere Datum, das zwei mögliche Versätze hat.
Eine andere Möglichkeit, dies zu erreichen, besteht darin, zu prüfen, ob ein Übergang stattfindet. Wir können das so machen:
Sie können überprüfen, ob es sich bei dem Übergang um eine Überlappung handelt, bei der es mehr als einen gültigen Offset für dieses Datum/diese Uhrzeit gibt, oder um eine Lücke, bei der dieses Datum/diese Uhrzeit für diese Zonen-ID nicht gültig ist – indem Sie Folgendes verwenden isOverlap() Und isGap() Methoden auf zot4.
Ich hoffe, dass dies den Leuten hilft, diese Art von Problemen zu bewältigen, sobald Java 8 allgemein verfügbar wird, oder denjenigen, die Java 7 verwenden und den JSR 310-Backport übernehmen.
14513100cookie-checkWarum führt die Subtraktion dieser beiden Epochenmillionen (im Jahr 1927) zu einem seltsamen Ergebnis?yes
Die eigentliche Antwort ist, immer, immer Sekunden seit einer Epoche für die Protokollierung zu verwenden, wie die Unix-Epoche, mit 64-Bit-Ganzzahldarstellung (mit Vorzeichen, wenn Sie Stempel vor der Epoche zulassen möchten). Jedes reale Zeitsystem weist ein nichtlineares, nicht monotones Verhalten wie Schaltstunden oder Sommerzeit auf.
– Phil H
12. Juli 2012 um 8:34
Ein tolles Video über solche Dinge: youtube.com/watch?v=-5wpm-gesOY
– Thorbjørn Ravn Andersen
14. Okt. 2014 um 10:39
Und noch einer vom selben Typ, @ThorbjørnRavnAndersen: youtube.com/watch?v=Uqjg8Kk1HXo (Schaltsekunden). (Dieser stammt von Tom Scotts eigenem YouTube-Kanal, nicht von Computerphile.)
– TRiG
3. Juli 2015 um 16:23
@Phil H „Sekunden seit der Epoche“ (dh Unix-Zeit) ist ebenfalls nichtlinear, in dem Sinne, dass POSIX-Sekunden keine SI-Sekunden sind und in der Länge variieren
– Erinnere dich an Monica
13. September 2020 um 23:57 Uhr
POSIX-Sekunden können in der Länge variieren, aber auch das Stepping ist eine zulässige und nicht ungewöhnliche Implementierungsoption. Das heißt, die Differenz zwischen Zeitstempeln im Abstand von einer Sekunde kann Null sein, wenn eine Schaltsekunde hinzugefügt wird, oder negativ, wenn die Zeitstempel weniger als eine Sekunde voneinander entfernt sind. Die Unix-Zeit verhält sich also nicht monoton wie andere reale Zeitsysteme und ist kein Allheilmittel.
– Erickson
4. Mai 2022 um 20:22 Uhr