Moment: Durch Hinzufügen und Subtrahieren von Datumsangaben werden keine Stunden beibehalten, wenn in der Zeitzone Mitternachts-Sommerzeitregeln gelten

Erstellt am 22. Aug. 2018  ·  8Kommentare  ·  Quelle: moment/moment

Beschreibung des Problems:

Ich versuche, einen Datumsbereich in (Array von Momentdaten) zu erstellen, in dem jedes Datum 1 Tag nach dem vorherigen liegt. Dies funktioniert wie erwartet für alle Benutzer, aber wir haben einen Fehlerbericht für einen Benutzer in der Zeitzone Amerika / Santiago erhalten, der doppelte Daten in der Benutzeroberfläche sieht. Was mir aufgefallen ist, dass das Hinzufügen von 1 Tag zu einem bestimmten Datum tatsächlich nur 23 Stunden hinzufügt.

Mit einem Datum auf 8/11/2018 00:00:00 in der Zeitzone Amerika / Santiago erhöht das Hinzufügen von 1 Tag die Zeit auf 8/11/2018 23:00:00 anstelle von 8/12/2018 00:00:00 .

In den Dokumenten wird erklärt, dass die Stunden beim Umschalten über eine Sommerzeit _ während der Verwendung von Tagen_ beibehalten werden sollten, aber dies scheint hier nicht der Fall zu sein.

Es sind auch besondere Überlegungen zu beachten, wenn Zeit hinzugefügt wird, die die Sommerzeit überschreitet. Wenn Sie Jahre, Monate, Wochen oder Tage hinzufügen, stimmt die ursprüngliche Stunde immer mit der hinzugefügten Stunde überein.

// This code is from moment.js docs https://momentjs.com/docs/#/manipulating/add/
var m = moment(new Date(2011, 2, 12, 5, 0, 0)); // the day before DST in the US
m.hours(); // 5
m.add(1, 'days').hours(); // 5

Schritte zum Reproduzieren

Im folgenden Code hat die verwendete Zeitzone die DST-Regel um Mitternacht. Ich habe ein Datum, das am Tag vor Inkrafttreten dieser Regel um Mitternacht liegt. Ich habe diesem Datum einen Tag hinzugefügt und erwarte, dass es am folgenden Tag um Mitternacht / 0 Stunden sein sollte (Stunden sollten beibehalten werden), aber es wird tatsächlich nur hinzugefügt 23 Stunden.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/11/2018 00:00:00'), 'America/Santiago');
fmt(x);                       // "2018-08-11T00:00:00-04:00 America/Santiago"
fmt(x.clone().add(1, 'day')); // "2018-08-11T23:00:00-04:00 America/Santiago" - offset unchanged, added 23 hours not 1 day
fmt(x.clone().add(2, 'day')); // "2018-08-13T00:00:00-03:00 America/Santiago" - original hour preserved now

Hier können Sie sehen, dass durch Hinzufügen von 24 Stunden die Zeit um 25 Stunden erhöht und der Zeitzonenversatz geändert wurde.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/11/2018 00:00:00'), 'America/Santiago');
fmt(x);                          // "2018-08-11T00:00:00-04:00 America/Santiago"
fmt(x.clone().add(24, 'hours')); // "2018-08-12T01:00:00-03:00 America/Santiago" - offset changed, added 25 hours
fmt(x.clone().add(48, 'hours')); // "2018-08-13T01:00:00-03:00 America/Santiago"

Umgebung:

  • Version 68.0.3440.84 (Official Build) (64-Bit)
  • Mac OSX El Capitan 10.11.6 (15G31)

Weitere hilfreiche Informationen:

  • Maschinenzeitzone: US Eastern Timezone, Sommerzeit
  • Der Zeit- und Datumscode wurde am 22. August 2018 um 15:00 Uhr ausgeführt
  • Ich habe dieses Problem in Chrome Dev Tools auf der Webseite von Moment.j reproduziert

JS-Datumsausgabe

(neues Datum ()). toString ()

  • Mi Aug 22 2018 15:13:40 GMT-0400 (Eastern Daylight Time)

(neues Datum ()). toLocaleString ()

  • 22.08.2008, 15:13:40 Uhr

(neues Datum ()). getTimezoneOffset ()

  • 240

navigator.userAgent

  • Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit / 537.36 (KHTML, wie Gecko) Chrome / 68.0.3440.84 Safari / 537.36

moment.version

  • 2.22.2
Bug DST Pending Next Release Up-For-Grabs

Hilfreichster Kommentar

Das Verhalten hier scheint zwischen moment-timezone v0.5.4 und v0.5.26 geändert worden zu sein.
In der alten Version haben Sie durch Hinzufügen von 1 Tag zu '2018-08-11' am selben Tag 23:00 Uhr erhalten
In der neuen Version erhalten Sie '2018-08-12' um Mitternacht. Diese Zeit existiert jedoch nicht und das Hinzufügen von 1 Minute geht eine Stunde zurück - aber diese DST-Änderung sollte 1 Stunde hinzufügen.

Alte Version, verhält sich wie in dieser Ausgabe beschrieben:
https://jsfiddle.net/eqyvuxht/1/

Neue Version, geändertes Verhalten, aber immer noch falsch, denke ich?
https://jsfiddle.net/0h6atn4b/4/

Hier ist eine Problemumgehung:

function switchZone(m, zone) {
  let arr = [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second()];
  if(zone) {
    return moment.tz(arr, zone);
  }
  return moment(arr);
}

function safeAddDays(m, days) {
   let oldZone = m.tz();
   let utc = switchZone(m, 'UTC');
   utc.add(days, 'days');
   return switchZone(utc, oldZone);
}

Alle 8 Kommentare

Ich habe versucht, dies in der Zeitzone Europa / Rom zu reproduzieren, und konnte nicht:

Europa / Rom - https://www.timeanddate.com/time/change/italy/rome

28. Oktober 2018 - Die Sommerzeit endet
Wenn die lokale Sommerzeit kurz vor dem Erreichen steht
Sonntag, 28. Oktober 2018, 3:00:00 Uhr Die Uhren werden 1 Stunde zurückgestellt
Sonntag, 28. Oktober 2018, 2:00:00 Uhr Ortszeit.

Dieser Code enthält keinen Fehler.

d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('10/27/2018 00:00:00'), 'Europe/Rome');
fmt(x)                       // "2018-10-27T06:00:00+02:00 Europe/Rome"
fmt(x.clone().add(1, 'day')) // "2018-10-28T06:00:00+01:00 Europe/Rome" - tz offset changed, hour preserved as expected
fmt(x.clone().add(2, 'day')) // "2018-10-29T06:00:00+01:00 Europe/Rome"

Sie können sehen, dass sich die TZ ändert, aber die Stunden konstant bleiben.

Basierend auf den Wiedergabeschritten sieht es so aus, als ob es eine Diskrepanz im Code gibt, wenn das hinzugefügte Datum perfekt auf dem DST-Cutoff landet. In der Zeitzone von Santiago gilt die Sommerzeitregel, dass die Uhren um Mitternacht eine Stunde zurückgehen.

Es scheint, dass dieser Randfall speziell für Zeitzonen gilt, in denen der DST-Grenzwert um Mitternacht liegt, da ich nicht mit einer Zeitzone reproduzieren konnte, in der der Grenzwert um 3 Uhr morgens liegt, mit einem Datum von x um 3 Uhr morgens.

Europa / Rom - https://www.timeanddate.com/time/change/italy/rome

28. Oktober 2018 - Die Sommerzeit endet
Wenn die lokale Sommerzeit kurz vor dem Erreichen steht
Sonntag, 28. Oktober 2018, 3:00:00 Uhr Die Uhren werden 1 Stunde zurückgestellt
Sonntag, 28. Oktober 2018, 2:00:00 Uhr Ortszeit.

Dieser Code enthält keinen Fehler.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('October 27, 2018 03:00:00'), 'Europe/Rome');
fmt(x);                       // "2018-10-27T09:00:00+02:00 Europe/Rome"
fmt(x.clone().add(1, 'days')) // "2018-10-28T09:00:00+01:00 Europe/Rome" - tz offset changed, hour preserved as expected
fmt(x.clone().add(2, 'days')) // "2018-10-29T09:00:00+01:00 Europe/Rome"

Ich konnte bestätigen, dass dieser Fehler nur für Zeitzonen besteht, wenn der Sommerzeit-Cutoff um Mitternacht liegt und Sie einem Datum Zeit hinzufügen, wodurch das Datum genau am Cutoff landet.

America / Punta_Arenas - https://www.timeanddate.com/time/zone/chile/punta-arenas
Im Jahr 2016 hatte diese Zeitzone auch um Mitternacht einen Cutoff.

Dieser Code scheint einen Fehler zu haben. Siehe Kommentar nach jeder Zeile

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/13/2016 00:00:00'), 'America/Punta_Arenas')
fmt(x);                        // "2016-08-13T00:00:00-04:00 America/Punta_Arenas"
fmt(x.clone().add(1, 'days')); // "2016-08-13T23:00:00-04:00 America/Punta_Arenas" - 23 hours added, not 1 day, no tz offset change
fmt(x.clone().add(2, 'days')); // "2016-08-15T00:00:00-03:00 America/Punta_Arenas"

Ich fand ein ähnliches Problem, wenn ich über die gleichen Zeitzonen subtrahierte (Sommerzeit um Mitternacht).

Wenn Sie die Daten genau von einer Sommerzeitschicht abziehen, bleibt die Stunde nicht nur an diesem Tag erhalten, sondern das Datum wird geändert.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/13/2018 23:00:00'), 'America/Santiago');
fmt(x);                             // "2018-08-14T00:00:00-03:00 America/Santiago"
fmt(x.clone().subtract(1, 'days')); // "2018-08-13T00:00:00-03:00 America/Santiago"
fmt(x.clone().subtract(2, 'days')); // "2018-08-12T01:00:00-03:00 America/Santiago" - hour not preserved, but date changed
fmt(x.clone().subtract(3, 'days')); // "2018-08-11T00:00:00-04:00 America/Santiago" - original hour preserved now

Wir haben einen weiteren Bericht zu diesem Problem von einem Benutzer erhalten, dessen Zeitzone America/Asuncion .

Die Funktion, die den Fehler in unserer Anwendung verursacht, sieht folgendermaßen aus:

function generateDayRange(start, end) {
    const days = [];
    let current = start.clone();

    while (current <= end) {
        days.push(current.clone());
        current = current.add(1, 'days');
    }

    return days;
}

Wenn wir diese Funktion verwenden, übergeben wir ein Startdatum, das am Anfang des Tages liegt, und ein Enddatum, bei dem die Zeit nicht so wichtig ist:

generateDayRange(startDate.clone().startOf('week'), startDate.clone().endOf('week'));
generateDayRange(getAppDate(startDate), getAppDate(endDate));
generateDayRange(startRange, startRange.clone().add(27, 'days'));

Das Problem ist, dass das resultierende Array von Momentdaten einen doppelten Tag enthält, was sich in der Benutzeroberfläche unserer Anwendung widerspiegelt (ein Kalender hat einen doppelten Tag).

Meine größere Sorge ist, dass, da sich die Bibliothek nicht wie erwartet verhält, subtilere Probleme unbemerkt bleiben können, wie z. B. ein wahrgenommener Datenverlust, schlechte Anfragen usw.

Ich werde daran arbeiten, eine Reihe von Tests in einem Codepen oder so etwas durchzuführen, damit neugierige Leser versuchen können, dieses Problem zu lösen.

Verwandte Ausgabe # 4785 enthält Links zum Code:
Moment Beispiel https://runkit.com/embed/1r62d83amq7x
dennoch handhabt luxon dies richtig
https://runkit.com/embed/t49achvensqf

Ich weiß, dass es kürzlich Änderungen an der Sommerzeit gab, daher sollten wir den Code überprüfen, der derzeit auf develop (aber nicht veröffentlicht wurde). Oder wir können bis zur nächsten Veröffentlichung warten, um zu sehen, ob die Dinge immer noch dieselben sind.

Das Verhalten hier scheint zwischen moment-timezone v0.5.4 und v0.5.26 geändert worden zu sein.
In der alten Version haben Sie durch Hinzufügen von 1 Tag zu '2018-08-11' am selben Tag 23:00 Uhr erhalten
In der neuen Version erhalten Sie '2018-08-12' um Mitternacht. Diese Zeit existiert jedoch nicht und das Hinzufügen von 1 Minute geht eine Stunde zurück - aber diese DST-Änderung sollte 1 Stunde hinzufügen.

Alte Version, verhält sich wie in dieser Ausgabe beschrieben:
https://jsfiddle.net/eqyvuxht/1/

Neue Version, geändertes Verhalten, aber immer noch falsch, denke ich?
https://jsfiddle.net/0h6atn4b/4/

Hier ist eine Problemumgehung:

function switchZone(m, zone) {
  let arr = [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second()];
  if(zone) {
    return moment.tz(arr, zone);
  }
  return moment(arr);
}

function safeAddDays(m, days) {
   let oldZone = m.tz();
   let utc = switchZone(m, 'UTC');
   utc.add(days, 'days');
   return switchZone(utc, oldZone);
}

Wir stoßen diesen Monat (September 2019) auf dieselbe Ausgabe, in der unsere Santiago-Benutzer, die unsere Datumsauswahl verwenden, einen Kalender mit den Datumsangaben 1, 2, 3, 4, 5, 6, 7, 7, 7, 7 für das sehen Monat September ... Unter der Haube verwenden wir auch .add(1, 'days') . Es wird nächstes Jahr im September 2020 wieder vorkommen, wenn wir bis dahin keine Lösung dafür haben.

@mblandfo Workaround scheint auch für uns den Trick zu tun. Vielen Dank!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen