Rrule: DTSTART mit TZID: dazwischen erzeugt falsche Zeit

Erstellt am 16. Juli 2019  ·  16Kommentare  ·  Quelle: jakubroztocil/rrule

Ein Problem melden

const ruleStr = [
'DTSTART;TZID=America/Los_Angeles:20190603T181500', 
'RRULE:FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,FR,SA'
].join('\n')
const rule = RRule.fromString(ruleStr)
const result = rule.between(
new Date('2019-07-16T07:00:00.000-07:00'), 
new Date('2019-07-23T07:00:00.000-07:00')
)
console.log(result)

gibt das aus:

0:Tue Jul 16 2019 15:15:00 GMT-0500 (CDT) {}
1:Wed Jul 17 2019 15:15:00 GMT-0500 (CDT) {}
2:Fri Jul 19 2019 15:15:00 GMT-0500 (CDT) {}
3:Sat Jul 20 2019 15:15:00 GMT-0500 (CDT) {}
4:Mon Jul 22 2019 15:15:00 GMT-0500 (CDT) {}

15:15:00 GMT-0500 (CDT) ist 17:15 PDT, während DTSTART DTSTART;TZID=America/Los_Angeles:20190603T181500

Wenn ich tzid setze, das von erhalte ich ein anderes Ergebnis:

const ruleStr = [
'DTSTART:20190603T181500', 
'RRULE:FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,FR,SA'
].join('\n')
const rule = RRule.fromString(ruleStr)
const ruleSet = new RRuleSet()
ruleSet.rrule(rule)
ruleSet.tzid('America/Los_Angeles')
const result = ruleSet.between(
new Date('2019-07-16T07:00:00.000-07:00'), 
new Date('2019-07-23T07:00:00.000-07:00')
)
console.log(result)
0:Tue Jul 16 2019 13:15:00 GMT-0500 (CDT) {}
1:Wed Jul 17 2019 13:15:00 GMT-0500 (CDT) {}
2:Fri Jul 19 2019 13:15:00 GMT-0500 (CDT) {}
3:Sat Jul 20 2019 13:15:00 GMT-0500 (CDT) {}
4:Mon Jul 22 2019 13:15:00 GMT-0500 (CDT) {}

Das erwartete Ergebnis ist:

0:Tue Jul 16 2019 20:15:00 GMT-0500 (CDT) {}
1:Wed Jul 17 2019 20:15:00 GMT-0500 (CDT) {}
2:Fri Jul 19 2019 20:15:00 GMT-0500 (CDT) {}
3:Sat Jul 20 2019 20:15:00 GMT-0500 (CDT) {}
4:Mon Jul 22 2019 20:15:00 GMT-0500 (CDT) {}

Was ist der Grund dafür?
Regelversion: 2.6.2
Ich führe den Knoten unter der CDT-Zeitzone aus. Meine Systemzeitzone ist America/Los_Angeles

Alle 16 Kommentare

Ich habe dieses Repo geklont und diesen Test hinzugefügt:

  it('generates correct recurrences when recurrence is WEEKLY and has BYDAY specified', () => {
    const rrule = new RRule({
      freq: RRule.WEEKLY,
      dtstart: new Date(Date.UTC(2019, 6, 17, 18, 0, 0)),
      tzid: 'America/Los_Angeles',
      count: 10,
      interval: 1,
      wkst: RRule.SU,
      byweekday: [RRule.MO, RRule.TU, RRule.WE, RRule.FR, RRule.SA]
    })

    expect(rrule.all()).to.deep.equal([
      new Date('2019-07-17T18:00:00.000-07:00'), // WE
      new Date('2019-07-19T18:00:00.000-07:00'), // FR
      new Date('2019-07-20T18:00:00.000-07:00'), // SA
      new Date('2019-07-22T18:00:00.000-07:00'), // MO
      new Date('2019-07-23T18:00:00.000-07:00'), // TU
      new Date('2019-07-24T18:00:00.000-07:00'),
      new Date('2019-07-26T18:00:00.000-07:00'),
      new Date('2019-07-27T18:00:00.000-07:00'),
      new Date('2019-07-29T18:00:00.000-07:00'),
      new Date('2019-07-30T18:00:00.000-07:00')
    ])
  })

Es ist nur erfolgreich, wenn ich es unter der UTC-Zeitzone ausführe (wenn ich "env": { "TZ": "UTC" } in launch.json festlege). Der Test schlägt in jeder anderen Zeitzone fehl.
@davidgoli ist das beabsichtigt und ich tzid richtig? Ich denke, es wurde hinzugefügt, um rrule mitzuteilen, dass es in einer angegebenen Zeitzone ausgeführt werden soll.

Ich habe das gleiche Problem, aber mit rrule.all(). UTC funktioniert, während jede andere Zeitzone den Offset erneut anwendet, wenn dies nicht der Fall sein sollte.

Ich denke, es könnte etwas mit Zeile 1849 von rrule-tz.js zu tun haben.

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded , im Gegensatz zu seinem Namen, führt keine Überprüfungen durch und ordnet immer um, es sei denn, es ist UTC.

function rezoneIfNeeded(date, options) { return new datewithzone_DateWithZone(date, options.tzid).rezonedDate(); }

Es sieht so aus, als würde das Datum neu eingeteilt, obwohl es bereits richtig eingeteilt wurde.

Das Ändern von rezonedDate() um einfach this.date scheint die Dinge für mich zu beheben. Ich kann nicht ganz sicher sein, ob ich woanders etwas kaputt gemacht habe, aber bei einem flüchtigen Blick sieht es so aus, als ob alles andere gut funktioniert, wenn diese Änderung vorgenommen wird.

Ich denke, es könnte etwas mit Zeile 1849 von rrule-tz.js zu tun haben.

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded , im Gegensatz zu seinem Namen, führt keine Überprüfungen durch und ordnet immer um, es sei denn, es ist UTC.

function rezoneIfNeeded(date, options) { return new datewithzone_DateWithZone(date, options.tzid).rezonedDate(); }

Es sieht so aus, als würde das Datum neu eingeteilt, obwohl es bereits richtig eingeteilt wurde.

Das ist nicht richtig. Die Methode rezonedDate überprüft, ob tzid ist, um zu bestimmen, ob ein Zonenversatz angewendet werden soll.

@agordeev Ich werde mich mit diesem Problem @hlee5zebra , bei der die Zeitzonenunterstützung einfach entfernt wurde, war nicht erforderlich. Erhalten Sie die erwarteten Ergebnisse, wenn Sie in beiden Fällen einfach nicht den Parameter tzid verwenden?

Unabhängig davon beunruhigt mich die Inkonsistenz zwischen dem Verhalten von RRule und RRuleSet und ist wahrscheinlich ein Fehler, der genauer untersucht werden muss. Danke für den Testfall!

Ich sehe das Problem, das zu meinem Problem geführt hat - mein Servercode hat den DTSTART-Wert nicht richtig verarbeitet und UTC übergeben, wenn er in die durch die TZID angegebene Zeitzone lokalisiert werden sollte. Vielleicht hilft dir das bei deinem Problem @agordeev . Danke @davidgoli, dass du mich in die richtige Richtung

Sieht so aus, als wäre ich über ein Problem in der Codebasis gestolpert. Die Methode toString() in datewithzone.ts gibt eine UTC-Zeit aus, wenn eine TZID angegeben wird, obwohl sie stattdessen die lokalisierte Zeit ausgeben sollte.

@hlee5zebra notieren Sie sich diesen Text in der README:

https://github.com/jakubroztocil/rrule#important -use-utc-dates

Wichtig: UTC-Daten verwenden

Datumsangaben in JavaScript sind knifflig. RRule versucht, so viel Flexibilität wie möglich zu unterstützen, ohne große erforderliche Abhängigkeiten von Drittanbietern hinzuzufügen, aber das bedeutet, dass wir auch einige spezielle Regeln haben.

Standardmäßig handelt RRule mit "Floating"-Zeiten oder UTC-Zeitzonen. Wenn Sie Ergebnisse in einer bestimmten Zeitzone wünschen, bietet RRule auch Zeitzonenunterstützung. Wie auch immer, der eingebaute "Zeitzonen"-Offset von JavaScript neigt dazu, nur im Weg zu stehen, sodass diese Bibliothek ihn einfach überhaupt nicht verwendet. Alle Zeiten werden mit Null-Offset zurückgegeben, als ob es in JavaScript nicht vorhanden wäre.

Die Quintessenz ist, dass die zurückgegebenen "UTC"-Daten immer als Daten in Ihrer lokalen Zeitzone interpretiert werden sollen. Dies kann bedeuten, dass Sie eine zusätzliche Konvertierung durchführen müssen, um die "richtige" Ortszeit mit angewendetem Offset zu erhalten.

Aus diesem Grund wird dringend empfohlen, Zeitstempel in UTC zu verwenden, z. neues Datum(Datum.UTC(...)). Die zurückgegebenen Daten werden ebenfalls in UTC angegeben (außer bei Chrome, das immer Daten mit einem Zeitzonen-Offset zurückgibt).

Der zusätzliche Nachteil besteht in den meisten JS-Implementierungen, Sie erhalten entweder einen UTC-Offset _oder_ einen lokalen Offset, können jedoch nicht zwischen ihnen wechseln. Auch dies variiert je nach Implementierung. So kann ein "UTC-Datum" mit einem toString() , das an einigen Stellen den Offset enthält und an anderen nicht.

In Chrome beispielsweise:

> new Date(Date.UTC(2016, 10, 5))
Fri Nov 04 2016 17:00:00 GMT-0700 (Pacific Daylight Time)

aber im Knoten:

> new Date(Date.UTC(2016, 10, 5))
2016-11-05T00:00:00.000Z

Um das beste Ergebnis zu erzielen, ignorieren Sie daher den toString() und verwenden Sie ausschließlich die Methoden toISOString() und getUTCHours() (usw.).

Beachten Sie, dass dieser Ansatz - nur UTC-Methoden für alle Datumsangaben zu verwenden und diese dann als Ortszeit zu "interpretieren" - einen einheitlichen Zugriff auf von rrule zurückgegebene Datums- und Uhrzeiten ermöglicht, _ohne die Zeitzone der rrule_ berücksichtigen zu müssen.

Okay, ich habe ein bisschen mit rrule.all() herumgebastelt, um zu versuchen, dieses Problem reproduzierbar zu machen, und ich habe Folgendes gefunden, um dieses Problem zuverlässig zu reproduzieren:

  1. Ich habe versucht, DTSTART als Mitternacht innerhalb der jeweiligen Zeitzonen von 'America/Adak', 'America/Chicago' (meine Ortszeit), 'America/New_York' und 'UTC' einzustellen, und das resultierende rrule.toString() wird genau gedruckt:
>> rRule.toString()
"DTSTART;TZID=America/Adak:20190718T000000
RRULE:FREQ=DAILY"

>> rRule.toString()
"DTSTART;TZID=America/Chicago:20190718T000000
RRULE:FREQ=DAILY"

>> rRule.toString()
"DTSTART;TZID=America/New_York:20190718T000000
RRULE:FREQ=DAILY"

>> rRule.toString()
"DTSTART:20190718T000000Z
RRULE:FREQ=DAILY"

Beim Durchlaufen der Iteratorfunktion, die an rrule.all() , stellt sich heraus, dass die erste Instanz des ersten Parameters date für jede Zeitzone die folgende ist, wenn jedes Date über .toISOString() :

Amerika/Adak:
"2019-07-18T04:00:00.000Z"

Amerika/Chicago:
"2019-07-18T00:00:00.000Z"

Amerika/New_York:
"2019-07-17T23:00:00.000Z"

KOORDINIERTE WELTZEIT:
"2019-07-18T00:00:00.000Z"

Wenn die Zeitzone nicht auf UTC eingestellt ist (zB America/Adak, America/New_York), sieht es so aus, als ob der Versatz zwischen Ihrer Ortszeit und der ausgewählten Zeitzone vom DTSTART-Datum abgezogen wird. Die New Yorker ISO-Zeichenfolge zeigt also 23:00 Uhr an, da der Versatz zwischen meiner Ortszeit und New York +1 ist, was, wenn wir von Mitternacht am 18.07.2019 abgezogen werden, zu dem führt, was wir sehen, nämlich 23:00 Uhr am 17.07.2019 /2019.

Beachten Sie, dass dies für UTC jedoch nicht der Fall ist, was seltsam ist.

Glauben Sie, dass dies das Problem sein könnte, oder muss eine Konfiguration vorgenommen werden, die ich möglicherweise übersehen habe?

Als Problemumgehung für das, was ich gesehen habe, habe ich Folgendes getan, und dies scheint mir zuverlässig eine genaue Instanz von JavaScripts Date :

rRule.all(function (date, i) {
    if (this.getSelectedTzid() !== 'UTC') {
        date = moment.tz({
            year: date.getUTCFullYear(),
            month: date.getUTCMonth(),
            date: date.getUTCDate(),
            hours: date.getUTCHours(),
            minutes: date.getUTCMinutes()
        }, this.getLocalTzid()).toDate();
    }

    ...
}.bind(this));

@hlee5zebra Ja, Ihr Ansatz zur Verwendung der getUTCxxx Methoden ist der richtige, der von der Readme empfohlen wird.

Denken Sie daran, dass ein Pseudo-UTC-Datum fast nie _wirklich_ in der UTC-Zeit liegt. UTC ist einfach überladen, um die "neutrale" Zeitzone zu sein, daher können die getUTCxxx Methoden verwendet werden, um die _lokale_ Zeit unabhängig von der ursprünglichen Zone abzurufen. Aus diesem Grund sollten Sie ohne die Verwendung von tzid dasselbe Verhalten erwarten, als würden Sie Ihre lokale Zeitzone in Ihrer lokalen Zeitzone verwenden. tzid sollte nur verwendet werden, um die aktuelle Ortszeit einer Wiederholung _in einer anderen Zeitzone_ abzurufen. Wenn Sie möchten, dass sich die Wiederholungen immer in der lokalen Zeitzone des Benutzers befinden, sollten Sie tzid .

Aus diesem Grund wird meine Neufassung dieser Bibliothek das eingebaute JS Date-Objekt überhaupt nicht verwenden. Es ist einfach zu verwirrend.

@agordeev Ich werde mich mit diesem Problem @hlee5zebra , bei der die Zeitzonenunterstützung einfach entfernt wurde, war nicht erforderlich. Erhalten Sie die erwarteten Ergebnisse, wenn Sie in beiden Fällen einfach nicht den tzid-Parameter verwenden?

Danke für deine Antwort David.

    const rrule = new RRule({
      freq: RRule.WEEKLY,
      dtstart: new Date(Date.UTC(2019, 6, 17, 18, 0, 0)),
      // tzid: 'America/Los_Angeles',
      count: 10,
      interval: 1,
      wkst: RRule.SU,
      byweekday: [RRule.MO, RRule.TU, RRule.WE, RRule.FR, RRule.SA]
    })

produziert:

  -  [Date: 2019-07-17T18:00:00.000Z]
  -  [Date: 2019-07-19T18:00:00.000Z]
  -  [Date: 2019-07-20T18:00:00.000Z]
  -  [Date: 2019-07-22T18:00:00.000Z]
  etc..

Die Daten/Zeiten sind also korrekt, aber die Zeitzone ist UTC. rrule berücksichtigt DTSTART in UTC, wenn ich tzid param weglasse.

Wenn Sie möchten, dass sich die Wiederholungen immer in der lokalen Zeitzone des Benutzers befinden, sollten Sie tzid nicht verwenden.

Ich dachte, die einzige Möglichkeit, Wiederholungen in der Zeitzone des Benutzers zu erhalten, besteht darin, diese Zeitzone an tzid param zu übergeben? Beachten Sie, dass die Bibliothek mit node.js auf dem Server verwendet wird.

Wenn die Wiederholung um 1800 in der Ortszeit des Benutzers erfolgt, _unabhängig von der Zeitzone_, müssen Sie tzid . Bitte vergewissern Sie sich, dass Sie die "floating"-Zeiten gelesen haben, wie in der README beschrieben: https://github.com/jakubroztocil/rrule#important -use-utc-dates

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

jimmywarting picture jimmywarting  ·  9Kommentare

michaelkrog picture michaelkrog  ·  9Kommentare

shorlbeck picture shorlbeck  ·  21Kommentare

Prinzhorn picture Prinzhorn  ·  15Kommentare

berardo picture berardo  ·  9Kommentare