Rrule: Sommerzeitumstellung wird trotz "Timezone Support" nicht korrekt behandelt

Erstellt am 20. Nov. 2018  ·  21Kommentare  ·  Quelle: jakubroztocil/rrule

Ich habe das Verhalten dieser Bibliothek (v2.5.6) am Frontend mit der entsprechenden PHP-Bibliothek (v2.3.3) am Backend verglichen. Es gibt eine definitive Inkonsistenz mit der Art und Weise, wie die Umstellung auf Sommerzeit gehandhabt wird, und ich glaube, dass die PHP-Version besser damit umgeht.

Codebeispiel:

In der Zeitzone America/Denver wird am Sonntag, dem 4. November 2018, auf Sommerzeit umgestellt (von GMT-6 auf GMT-7). Lassen Sie uns also eine wiederkehrende Serie einrichten, die um 13:00 Uhr beginnt und sich jeden Mo, Mi, Do wiederholt, beginnend am 1. November, der vor der Zeitumstellung liegt:

RRule.fromString(
  "DTSTART;TZID=America/Denver:20181101T190000;\n"
  + "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=7"
).all()

In diesem Fall würde ich erwarten, dass alle Wiederholungen wie unten beschrieben um 13:00 Uhr beginnen. (Laut den Dokumenten verwende ich die Luxon-Bibliothek. Ich habe nichts gesehen, was besagt, dass ich sie irgendwie initialisieren musste, also gehe ich davon aus, dass sie über Garn korrekt "installiert" ist.)

Erwartetes Ergebnis (alle ab 13:00:00):

(7) [
    Thu Nov 01 2018 13:00:00 GMT-0600 (Mountain Daylight Time),
    Mon Nov 05 2018 13:00:00 GMT-0700 (Mountain Standard Time),
    Wed Nov 07 2018 13:00:00 GMT-0700 (Mountain Standard Time),
    Thu Nov 08 2018 13:00:00 GMT-0700 (Mountain Standard Time),
    Mon Nov 12 2018 13:00:00 GMT-0700 (Mountain Standard Time),
    Wed Nov 14 2018 13:00:00 GMT-0700 (Mountain Standard Time),
    Thu Nov 15 2018 13:00:00 GMT-0700 (Mountain Standard Time)
]

Tatsächliche Ergebnis:

(7) [
    Thu Nov 01 2018 13:00:00 GMT-0600 (Mountain Daylight Time),
    Mon Nov 05 2018 12:00:00 GMT-0700 (Mountain Standard Time), <-- Should be 13:00:00
    Wed Nov 07 2018 12:00:00 GMT-0700 (Mountain Standard Time), <-- same
    Thu Nov 08 2018 12:00:00 GMT-0700 (Mountain Standard Time), <-- same
    Mon Nov 12 2018 12:00:00 GMT-0700 (Mountain Standard Time), <-- same
    Wed Nov 14 2018 12:00:00 GMT-0700 (Mountain Standard Time), <-- same
    Thu Nov 15 2018 12:00:00 GMT-0700 (Mountain Standard Time)  <-- same
]

Als ich eine ähnliche Situation in der PHP-Bibliothek einrichtete, war das Ergebnis wie oben erwartet, wobei alle Instanzen um 13:00:00 in der Zeitzone America/Denver begannen, nicht UTC .

Andere Details:

  • Version 2.5.6
  • Mac OS X 10.13.6
  • Chrom 70
  • Meine aktuelle Ortszeit ist MST (Amerika/Denver, GMT-7)

Edit: Tippfehler korrigieren

Alle 21 Kommentare

Bekommst du Konsolenwarnungen? z.B:

'Using TZID without Luxon available is unsupported. Returned times are in UTC, not the requested time zone'

Luxon ist in optionalDependencies , also wird es importiert, wenn es in Ihrem node_modules ist, aber ansonsten nicht.

Wenn ich Ihren Code in der rrule-Testsuite ausführe, erhalte ich:

      [
        [Date: 2018-11-01T19:00:00.000Z]
        [Date: 2018-11-05T19:00:00.000Z]
        [Date: 2018-11-07T19:00:00.000Z]
        [Date: 2018-11-08T19:00:00.000Z]
        [Date: 2018-11-12T19:00:00.000Z]
        [Date: 2018-11-14T19:00:00.000Z]
        [Date: 2018-11-15T19:00:00.000Z]
      ]

was ich erwarten würde, da die von Ihnen angeforderte Zeit 1900 in der Zeitzone von Denver ist. (Da dies JavaScript und nicht PHP ist, können wir Ihnen keine Date -Objekte in einer anderen Zeitzone als der Zeitzone Ihres lokalen Computers oder UTC geben, daher verwendet diese Bibliothek immer UTC.)

Die Tatsache, dass Sie lokale (MST/MDT) Daten und keine UTC-Daten sehen, ist für mich etwas verwirrend. Soweit ich weiß, ist diese Bibliothek derzeit so konfiguriert, dass sie immer nur UTC-Daten zurückgibt, obwohl sie früher stattdessen lokale Daten zurückgab.

Bekommst du Konsolenwarnungen? z.B:

„Die Verwendung von TZID ohne verfügbares Luxon wird nicht unterstützt. Zurückgegebene Zeiten sind in UTC, nicht in der angeforderten Zeitzone.

Keine Konsolenwarnungen. Es scheint, dass Luxon ordnungsgemäß bereitgestellt wird.

In Bezug auf die Zeitzonen erhalte ich MST/DST, wenn ich meinen Code direkt in der Befehlszeile der Chrome-Devtools-Konsole ausführe.

Schritte: Öffnen Sie Chrome --> Navigieren Sie zur Seite mit bereitgestelltem RRule und Luxon --> Cmd+Opt+I --> Registerkarte "Konsole" --> Kopieren Sie das obige Snippet und fügen Sie es unten in die Befehlszeile ein --> Eingabetaste

Diese Bibliothek ist derzeit so konfiguriert, dass sie immer nur UTC-Daten zurückgibt, obwohl sie früher stattdessen lokale Daten zurückgab.

Wisst ihr ab welcher Version sich das geändert hat? Warum sollte diese Änderung vorgenommen werden? Wenn nur UTC-Daten zurückgegeben werden, führt dies tatsächlich zu dieser Abweichung von der Sommerzeit! Ein Ereignis, das immer um 13:00 UTC stattfindet, garantiert ziemlich genau, dass das Ereignis zu einer anderen Zeit vor/nach einer Umstellung auf Sommerzeit beginnt. Ich denke, diese Änderung sollte rückgängig gemacht werden, oder es sollte eine andere alternative Methode bereitgestellt werden, die es ermöglicht, die Sommerzeit zu berücksichtigen. UTC ist äußerst nützlich, aber nicht immer in allen Situationen der richtige Ansatz. Bei der Arbeit mit wiederkehrenden Ereignissen MÜSSEN wir die spezifische Zeitzone berücksichtigen, in der das Ereignis auftritt, insbesondere weil die Sommerzeit eine Sache ist.

Tatsächlich bekomme ich ein anderes Verhalten von Chrome als von Node.

Ich würde sagen, ich verstehe nicht, warum Ihre angegebene "erwartete" Ausgabe das ist, was Sie wirklich erwarten. Diese Zeiten erscheinen falsch.

Was richtig wäre, wären die Zeiten, wenn Sie den TZ-Offset für jede Zeit zur angegebenen Ortszeit addieren würden. Dann sind alle Zeiten 19:00 Uhr. Aus diesem Grund handelt RRule in UTC und warum die Verwendung von UTC nichts mit diesem speziellen Fehler zu tun hat.

In der Tat, wenn Sie dies tun:

const dates = RRule.fromString(
  "DTSTART;TZID=America/Denver:20181101T190000;\n"
  + "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=7"
).all()
dates.map((date) => date.toISOString())

Sie sehen die resultierenden ISO-Strings mit konsistent den richtigen Datums- und Uhrzeitangaben.

Es scheint außerdem, dass Chrome die Rückgabe von Datumsangaben in UTC überhaupt nicht unterstützt:

> new Date(Date.UTC(2016, 10, 5))
Fri Nov 04 2016 17:00:00 GMT-0700 (Pacific Daylight Time)
// ^ I asked for a date in UTC, not in PDT!

RRule verwendet Luxon, um die korrekte Ortszeit für _alle Zeitzonen der Welt_ zu generieren, nicht nur für Ihre lokale Zeitzone. Aus diesem Grund ist die Verwendung von UTC von entscheidender Bedeutung: Wenn der Zeitzonencode "richtige" Daten für Ihre lokale Zeitzone zurückgibt, Sie jedoch Daten in einer anderen Zeitzone generieren müssen (angegeben mit dem Parameter TZID ), würden Sie erhalten völlig falsche Ergebnisse. Wir verwenden „UTC-Daten“ nicht, weil wir DST-Berechnungen in UTC durchführen (das tun wir nicht, Sie können die Testsuite einsehen und viele Beispiele für korrekte DST-Berechnungen sehen), sondern weil die Zeitzonen in der JavaScript-Spezifikation schrecklich sind defekt, und UTC kommt einem verfügbaren "neutralen" Datumskonzept am nächsten. Es tut mir leid, dass es verwirrend ist, damit zu arbeiten, und dass die Verwendung durch diese Bibliothek nicht klarer sein kann (obwohl ich für Vorschläge offen bin).

Was _besorgniserregend ist, ist das Problem mit der Browserkompatibilität, das Sie ansprechen. Ich möchte Sie bitten, jetzt Ihren eigenen Beispielcode in einer laufenden Node-Konsole oder in diesem Fall in Firefox auszuprobieren. Sie werden sehr unterschiedliche (und korrekte) Ergebnisse sehen.

Das eigentliche Problem hier ist also, dass die Ergebnisse in Chrome verwirrend sind. Ich denke, das ist es wert, darauf einzugehen, obwohl mir nicht sofort klar ist, wie das genau geht. Was ich Ihnen sagen _kann_ ist, dass beide Umgebungen genau die gleichen ISO-Strings und Zeitstempel generieren.

@davidgoli Du hast Recht, Firefox zeigt ein anderes Ergebnis in der Konsole an, wenn mein Originalcode ausgeführt wird. Das Ergebnis ist jedoch letztendlich dieselbe Zeit, es ist nur so, dass das Ergebnis in Firefox in UTC-Zeit ausgedrückt wird, während es in Chrome in America/Denver -Zeit ausgedrückt wird (oder wahrscheinlich was auch immer Ihre lokale Zeitzone ist).

Ich würde sagen, ich verstehe nicht, warum Ihre angegebene "erwartete" Ausgabe das ist, was Sie wirklich erwarten. Diese Zeiten erscheinen falsch.

Korrekt wären die Zeiten, wenn Sie den TZ-Offset für jede Zeit zur angegebenen Ortszeit addieren würden. Dann sind alle Zeiten 19:00 Uhr. Aus diesem Grund handelt RRule in UTC und warum die Verwendung von UTC nichts mit diesem speziellen Fehler zu tun hat.

Lassen Sie mich versuchen zu erklären, warum wir NICHT 19:00 UTC-Zeit für jedes wiederkehrende Ereignis wollen.

Angenommen, ein Benutzer möchte jeden Mittwoch um 13:00 Uhr ein sich wiederholendes Ereignis an einem bestimmten Ort irgendwo in der Zeitzone America/Denver planen. Einfach ausgedrückt bedeutet dies – egal zu welcher Jahreszeit – der Benutzer erwartet, dass dasselbe Ereignis jeden Mittwoch um 13:00 Uhr stattfindet, wenn es Mittwoch um 13:00 Uhr an diesem Ort ist . Nicht 12 Uhr, nicht 14 Uhr. Immer um 13:00 Uhr für Amerika/Denver-Zeit.

rrule löst die Wiederholungen jedoch gemäß UTC-Zeit auf – selbst wenn eine Zeitzone übergeben wird. Dadurch wird im Grunde gesagt: „Oh, 13:00 Uhr Amerika/Denver ergibt 19:00 Uhr. Daher 19:00 Uhr muss sich in Amerika/Denver IMMER auf 13:00 Uhr beziehen, das ganze Jahr über.Aber das stimmt einfach nicht. Vor dem 4. November 2018 stimmt das, aber nach dem 4. November wird in Amerika/Denver die Sommerzeit aufgehoben, sodass 19:00 UTC-Zeit nun 12:00 Uhr Amerika/Denver-Zeit entspricht . Unserem Benutzer wird also fälschlicherweise mitgeteilt, dass das Ereignis um 12:00 Uhr und nicht um 13:00 Uhr stattfindet.

Mit anderen Worten, 13 Uhr Amerika/Denver kann entweder 19:00 oder 20:00 UTC sein, je nachdem, ob wir Sommerzeit haben oder nicht, und es ist unmöglich, rrule zu verwenden, um die richtigen Wiederholungen zu erhalten, weil es sagen möchte, dass 19: 00 ist das ganze Jahr über die richtige Zeit.

Ich habe das Gefühl, rrule glaubt an den Mythos, an den die meisten Programmierer glauben, dass UTC immer die richtige Lösung ist, wenn es um Datums- und Zeitangaben geht. Aber es gibt bestimmte Fälle, in denen dies nicht die beste Lösung ist, und dieser Fall (Wiederholung zukünftiger Ereignisse an einem bestimmten Ort) ist einer davon.


Bearbeiten: Theoretisch bin ich damit einverstanden, dass rrule die Zeiten in UTC zurückgibt, aber diese Zeiten sollten für die angegebene Zeitzone korrekt sein, wenn eine Zeitzone übergeben wird. Das bedeutet, dass wir sehen sollten, dass 19:00 UTC um 20:00 UTC wechselt Irgendwann in der Ergebnismenge, für das hier diskutierte Beispiel.

Lassen Sie mich hier oben zurückkommen - ich denke, das ist ein Dokumentationsproblem.

RRule _immer_ gibt JS "UTC"-Daten zurück. Dies bedeutet jedoch nicht, dass diese Daten die UTC-Zeit darstellen sollen. Vielmehr wurde aufgrund der Einschränkungen der einzigen Alternative - lokale Daten - die Entscheidung getroffen, dass _nur_ JS-Daten mit einem Zeitzonen-Offset von 0 für die Datumsberechnung dieser Bibliothek verwendbar sind. Dies hat den irreführenden Nebeneffekt, dass es den Anschein hat, dass diese Zeiten tatsächlich die Zeit in UTC darstellen, obwohl sie tatsächlich in der Zeitzone interpretiert werden sollen, die durch den Parameter TZID dargestellt wird. In Firefox ist das beabsichtigte Verhalten beispielsweise, dass die Bibliothek Ihnen ein Datum zurückgibt, das 19:00 darstellt, und da Sie diese Zeit als Denver-Zeit angegeben haben, wird sie für die Denver-Zeit korrekt sein. Sie sollten die Tatsache ignorieren, dass dieses Datum anzeigt, dass es sich um ein UTC-Datum handelt, da dies die einzige Art von Datum ist, die in JavaScript nützlich ist. Stattdessen sollten Sie das Wissen verwenden, dass diese Regel die Denver-Zeitzone hat, um 19:00 als Ortszeit in Denver zu interpretieren.

Dies ist klarer, wenn Sie Zeitzonen außerhalb der Zone Ihres lokalen Computers verwenden. Wenn Sie Ihre RRule beispielsweise stattdessen mit dem Zonennamen America/New_York einrichten, sehen Sie, dass die Daten als 17:00 zurückgegeben werden, da dies die Ortszeit in der Zeitzone Ihres Computers (Denver) ist, wenn es 19 ist :00 in der gewünschten Zeitzone (New York).

JavaScript hat keine Möglichkeit, ein Datum an eine bestimmte Zeitzone zu "heften", daher stellen wir immer das richtige Datum und die richtige Uhrzeit dar, egal was passiert, und Sie können die Tatsache verwerfen, dass das Datum sich selbst als UTC meldet. Wenn Zeitzonen in RRule verwendet werden, handelt es sich tatsächlich um eine Ortszeit. Aus diesem Grund gibt Ihnen Firefox die korrekte Ortszeit als 19:00 an (obwohl dieses Datum als UTC gemeldet wird, ist es tatsächlich Denver-Zeit).

Klärt das überhaupt etwas auf? Ich verstehe, dass es verwirrend und nicht intuitiv ist, aber hoffentlich ist es einfach genug, um zu verstehen, dass es leicht wird, darüber nachzudenken, sobald es verstanden wurde. Das sollten wir auf jeden Fall besser dokumentieren.

Eine Sache, die ich daraus untersuchen möchte, ist die Möglichkeit, Luxon zu verwenden, um die resultierenden Daten in die lokale Zeitzone zu verschieben, damit sie zumindest Ihren Erwartungen entsprechen. Ich denke, das sollte nicht zu schwer sein und dazu beitragen, Missverständnisse wie dieses zu klären. Das Chrome-Problem ist wirklich neu für mich und es sind schlechte Nachrichten.

Dies wäre jedoch eine bahnbrechende Änderung, daher möchte ich vor der Veröffentlichung Gelegenheit für Community-Feedback bieten.

@shorlbeck Dieser Unterschied implementiert meiner Meinung nach das Verhalten, das Sie erwarten:

diff --git a/src/datewithzone.ts b/src/datewithzone.ts
index 8ae3ed0..d9b917c 100644
--- a/src/datewithzone.ts
+++ b/src/datewithzone.ts
@@ -38,7 +38,10 @@ export class DateWithZone {

       const rezoned = datetime.setZone(this.tzid!, { keepLocalTime: true })

-      return rezoned.toJSDate()
+      return rezoned
+        .toUTC()
+        .setZone('local', { keepLocalTime: true })
+        .toJSDate()
     } catch (e) {
       if (e instanceof TypeError) {
         console.error('Using TZID without Luxon available is unsupported. Returned times are in UTC, not the requested time zone')
diff --git a/test/rrule.test.ts b/test/rrule.test.ts
index 7774b8a..a794e02 100644
--- a/test/rrule.test.ts
+++ b/test/rrule.test.ts
@@ -3804,4 +3804,17 @@ describe('RRule', function () {
     expect(() => rule.between(invalidDate, validDate)).to.throw('Invalid date passed in to RRule.between')
     expect(() => rule.between(validDate, invalidDate)).to.throw('Invalid date passed in to RRule.between')
   })
+
+  it('#300', () => {
+    const rule = RRule.fromString(
+      "DTSTART;TZID=America/Denver:20181101T190000;\n"
+      + "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=3"
+    )
+
+    expect(rule.all()).to.deep.equal([
+      DateTime.utc(2018, 11, 2, 1, 0, 0).toJSDate(),
+      DateTime.utc(2018, 11, 6, 2, 0, 0).toJSDate(),
+      DateTime.utc(2018, 11, 8, 2, 0, 0).toJSDate(),
+    ])
+  })
 })

Ich bin jedoch nicht ganz davon überzeugt, dass dies der richtige Weg für diese Bibliothek ist. Angesichts des allgemeinen Ansatzes der Bibliothek, "gleitende" Zeiten zurückzugeben, die immer in der lokalen Zeitzone des Clients interpretiert werden sollen (allerdings mit einem Offset von 0). Die geeigneten Getter für die von rrule zurückgegebenen Daten sind die Getter getUTC* . Wenn Sie also beispielsweise getUTCHours() für jedes Datum in Ihrem ursprünglichen Ergebnis verwenden, erhalten Sie die korrekte Stunden, sogar in Chrome.

Ich neige dazu zu denken, dass die richtige Lösung hier nicht darin besteht, das Verhalten dieser Bibliothek zu ändern, sondern ihre etwas eigenwillige Verwendung von Pseudo-"UTC"-Zeiten (eigentlich "schwebend") klarer zu dokumentieren. Ich bin jedoch offen für Überzeugungsarbeit.

Ich habe die README-Datei mit einigen Erläuterungen und Anweisungen für Ihren Anwendungsfall aktualisiert. Ich bin jedoch daran interessiert, diese Diskussion fortzusetzen, und bin offen dafür, dieses Verhalten möglicherweise in einer zukünftigen Version der Bibliothek zu ändern.

Danke für die Antwort und die ausführliche Erklärung.

Leider war ich noch nie verwirrter.

Das Arbeiten mit Daten und Zeiten ist schon schwer genug, wenn diese Datumszeiten entweder tatsächlich UTC oder tatsächlich eine Ortszeit sind, aber auch daran denken zu müssen, dass manchmal eine UTC-Zeit nicht wirklich eine UTC-Zeit ist, ist mehr, als mein Gehirn verarbeiten kann. Ich habe heute den ganzen Tag damit verbracht, zu verstehen, was Sie geschrieben haben, aber am Ende des Tages ist es so völlig kontraintuitiv, dass ich mich einfach nicht damit befassen kann.

Immer wenn ich mir den Code ansehe und ein "UTC" -Datum sehe, kann ich mich nicht erinnern, ob es sich tatsächlich um UTC oder nur um Pseudo-UTC handelt, insbesondere wenn Sie das Chrome-Problem dort hinzufügen. Das macht es mir fast unmöglich zu codieren. Es ruiniert mein Vertrauen in die Lesbarkeit meines Codes.

Hier ist also meine Frage ... Zurück zu meinem ursprünglichen Beispiel ... In echter UTC bezieht sich 2018-11-01 19:00:00 auf 13:00 Uhr in Amerika / Denver-Zeit (13:00 Uhr). Und in meinem Beispiel ist 13:00 Uhr Amerika/Denver die richtige Zeit für mein Ereignis. Aber wenn die UTC nicht die wahre UTC ist, bedeutet das, dass ich new Date(Date.UTC(2018, 10, 1, 13, 0, 0)) oder new Date(Date.UTC(2018, 10, 1, 19, 0, 0)) übergeben sollte, wenn ich den Parameter tzid verwende? (Die Differenz beträgt 13:00 vs. 19:00)

Geht Pseudo-UTC sowohl ein als auch aus? Oder geht echte UTC ein, aber Pseudo-UTC kommt heraus? Und wie kann ich mir das jemals zuverlässig merken??

Ihre Regel ist angegeben als:

DTSTART;TZID=America/Denver:20181101T190000
RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=7

Aber gemäß der RRULE RFC5545 weist dies auf eine Regel in der Denver-Zeitzone mit der Ortszeit 1900 hin: https://tools.ietf.org/html/rfc5545#section -3.3.5

For example, the following represents 2:00 A.M. in New York on January 19, 1998:

       TZID=America/New_York:19980119T020000

Wenn Sie einen DTSTART um 13:00 Uhr Ortszeit in Denver wünschen, sollten Sie ihn in Ihrer Regel als 13:00 Uhr angeben. Die Bibliothek sollte diesbezüglich der Spezifikation vollständig entsprechen. (Nun, abgesehen von der kleinen Tatsache, dass es 1300 UTC zurückgibt ...)

Lesen Sie außerdem unbedingt den Abschnitt über "Schwebezeit":

FORM #1: DATE WITH LOCAL TIME

      The date with local time form is simply a DATE-TIME value that
      does not contain the UTC designator nor does it reference a time
      zone.  For example, the following represents January 18, 1998, at
      11 PM:

       19980118T230000

      DATE-TIME values of this type are said to be "floating" and are
      not bound to any time zone in particular.  They are used to
      represent the same hour, minute, and second value regardless of
      which time zone is currently being observed.  For example, an
      event can be defined that indicates that an individual will be
      busy from 11:00 AM to 1:00 PM every day, no matter which time zone
      the person is in.  In these cases, a local time can be specified.

JavaScript bietet leider keine Implementierung von "floating time". Die nächste Option, die wir zu einer "reinen", zeitzonenlosen Zeit haben, ist UTC, daher "Pseudo-UTC". (Fürs Protokoll: rrule unterstützt auch Gleitzeiten, die weder mit einer TZID noch mit einer Z UTC-Bezeichnung angegeben werden.)

Die Umstellung auf UTC wurde mit diesem Commit vollständig begonnen: https://github.com/jakubroztocil/rrule/commit/850ed075175eb1acfcbd7b2cddf0606f2b2206f7

Wenn Sie sich den vorherigen Commit ansehen und den hinzugefügten Test aus #850ed075 auswählen, werden Sie feststellen, dass er fehlschlägt. Tatsächlich schlugen Hunderte von Tests fehl, es sei denn, die gesamte Suite wurde in UTC ausgeführt. Das liegt daran, dass wir zur Berechnung zukünftiger Daten, die über die Sommerzeitgrenzen hinausgehen, Daten instanziieren mussten, als die aktuelle Uhr in Sommerzeit lief, und dann mathematische Berechnungen durchführen mussten, die uns in die Standardzeit versetzten, was zu einem falschen 1-Stunden-Offset führte. Die aktuelle Implementierung gibt immer korrekte lokale Zeiten an. Jetzt wird die gesamte Testsuite tatsächlich in jeder lokalen Umgebung bestehen, mit der Einschränkung, dass Sie die Methoden getUTCDate() / getUTCHour() verwenden müssen, um das richtige Datum/die richtige Stunde zu erhalten.

Ich untersuche, dies in einer zukünftigen Version zu ändern, aber die Änderung ist nicht trivial, ohne eine erforderliche Bibliothek eines Drittanbieters wie Luxon zu übernehmen.

In einer kommenden Version würde ich gerne das Verhalten ändern, um Daten zurückzugeben, die in Ihrer lokalen Zeitzone immer korrekt sind, also zum Beispiel:

  • DTSTART;TZID=America/Denver:20181101T130000 würde Ihnen ein Datum zurückgeben, das 1300-0700 (Standardzeit) ist, wenn Sie sich in Denver befinden, oder 2000Z , wenn Sie sich in UTC befinden
  • DTSTART=20181101T130000 würde Ihnen 1300-0700 geben, wenn Sie sich in Denver befinden, und 1300Z , wenn Sie sich in UTC befinden
  • DTSTART=20181101T130000Z würde Ihnen 0600-0700 geben, wenn Sie sich in Denver befinden, und 1300Z , wenn Sie sich in UTC befinden

Ist das das Verhalten, das Sie erwarten?

Ein großes Problem ist, dass eine bestimmte lokale JS-Umgebung nur Daten in einer einzigen Zeitzone darstellen kann. Daher sind weder new Date(...) noch new Date(Date.UTC(...)) wirklich geeignet, wenn Sie ein Datum und eine Uhrzeit in einer bestimmten Zeitzone darstellen möchten (die möglicherweise von Ihrer aktuellen Zeitzone abweicht).

Also, was soll das Verhalten sein mit:

new RRule({
  dtstart: new Date(2018, 10, 1, 10, 0, 0),
  tzid: 'America/Denver'
})

wenn sich die lokale Maschine in America/Los_Angeles befindet? Sie müssten sich zumindest des 1-stündigen Unterschieds zwischen der Zeit, die Sie erstellen, und der Zeitzone, in der Sie diese Zeit haben möchten, bewusst sein. Soll es also 11 Uhr (Denver-Zeit, die angeforderte Zone) oder 10 Uhr ( Zeit von Los Angeles, Ursprungszone)? Ich denke, Sie könnten beides vertreten.

Umständlicherweise unterscheidet sich ein Date -Objekt sowohl von einem ISO-Datums-String als auch von einem DTSTART-String darin, dass es einen Zeitstempel in UTC-Millisekunden darstellt, der kein Konzept von Zeitzone oder Offset hat. Daher denke ich, dass es am sinnvollsten ist, den Zeitstempel der gewünschten Zeit _in der richtigen Zone_ darzustellen, was bedeutet, wenn Sie in Los Angeles sind und die Zeit in Denver darstellen möchten, müssen Sie ein Datumsobjekt übergeben, das mit dem relativen initialisiert wurde Offset angewendet (das obige Beispiel würde also 11:00 Uhr in Denver darstellen). Das ist allerdings verwirrend, denn wenn man dann nach New York reist und gleichzeitig darstellen will, muss man new Date(2018, 10, 1, 13, 0, 0) schreiben!

Ein Vorteil, das gesamte Konzept der lokalen Daten aus dem Fenster zu schmeißen, ist, dass Sie sich nicht damit befassen müssen; Das dargestellte Datum und die Uhrzeit können als Gleitzeit behandelt werden, die in jede Zeitzone projiziert werden kann, ohne eine Konvertierung vorzunehmen.

Dies wird natürlich durch das Verhalten von Engines wie Chrome erschwert.

Für Ihre Zwecke können Sie wahrscheinlich die gewünschten Werte erhalten, indem Sie Folgendes tun:

rule.all().map(d => new Date(
    d.getUTCFullYear(),
    d.getUTCMonth(),
    d.getUTCDate(),
    d.getUTCHours(),
    d.getUTCMinutes(),
    d.getUTCMilliseconds()
  ))

Ich versuche gerade zu entscheiden, ob das in die eigentliche Bibliothek gehört ...

Danke! Deine Antwort hat mir wirklich einiges klar gemacht. Ich glaube, ich war gestern müde und ausgebrannt, als ich versuchte, alles zu verstehen. Ich sehe jetzt, was passiert, und Sie haben Recht, ich habe die falsche Zeit übergeben, als ich den Parameter tzid verwendet habe.

Für Ihre Zwecke können Sie wahrscheinlich die gewünschten Werte erhalten, indem Sie Folgendes tun:

rule.all().map(d => new Date(
    d.getUTCFullYear(),
    d.getUTCMonth(),
    d.getUTCDate(),
    d.getUTCHours(),
    d.getUTCMinutes(),
    d.getUTCMilliseconds()
  ))

Ich versuche gerade zu entscheiden, ob das in die eigentliche Bibliothek gehört ...

Das hat bei mir funktioniert! Vielen Dank. Es wäre schön, wenn es vielleicht eine Möglichkeit gäbe, zu konfigurieren, was von rule.all() und rule.between() zurückgegeben wird, damit wir nicht noch eine weitere Breaking Change einführen, aber trotzdem das obige Verhalten nativ erhalten können. Sie sind sich nicht sicher, wie Sie das erreichen sollen, wenn Sie auch bedenken, dass ich gerne rrulestr() oder RRule.fromString() verwende, aber vielleicht ein zusätzliches Argument oder etwas, um anzugeben, welche Art von Datum zurückgegeben wird?


UPDATE: Das obige d.getUTCMilliseconds() sollte in d.getUTCSeconds() geändert werden

Ihre Lösung funktioniert also, wenn die Zeitzone meines Systems mit der angeforderten Zeitzone übereinstimmt, aber ich habe eine schreckliche Zeit damit, herauszufinden, wie ich RRule dazu bringen kann, die Daten in der angeforderten Zeitzone zurückzugeben, wenn sie sich von der Systemzeitzone unterscheidet. Es gibt immer Daten zurück, die in der Systemzeitzone dargestellt werden, anstatt in der angeforderten Zeitzone. Beispiel:

  1. Stellen Sie die Zeitzone Ihres Betriebssystems auf America/Denver ein
  2. Geben Sie in der Chrome-Devtools-Konsole Folgendes ein:
RRule.fromString(
    "DTSTART;TZID=America/Denver:20181101T130000;\n"
    + "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=7"
).all().map(d => new Date(
    d.getUTCFullYear(),
    d.getUTCMonth(),
    d.getUTCDate(),
    d.getUTCHours(),
    d.getUTCMinutes(),
    d.getUTCSeconds()
));

Ergebnis ist korrekt (für das, was ich erwarte/brauche):

(7) [Thu Nov 01 2018 13:00:00 GMT-0600 (Mountain Daylight Time), Mon Nov 05 2018 13:00:00 GMT-0700 (Mountain Standard Time), Wed Nov 07 2018 13:00:00 GMT-0700 (Mountain Standard Time), Thu Nov 08 2018 13:00:00 GMT-0700 (Mountain Standard Time), Mon Nov 12 2018 13:00:00 GMT-0700 (Mountain Standard Time), Wed Nov 14 2018 13:00:00 GMT-0700 (Mountain Standard Time), Thu Nov 15 2018 13:00:00 GMT-0700 (Mountain Standard Time)]
  1. Ändern Sie die Zeitzone des Betriebssystemsystems in America/New_York
  2. Führen Sie denselben Befehl in der Chrome-Devtools-Konsole aus.

Das Ergebnis unterscheidet sich, da es in der Systemzeitzone dargestellt wird:

(7) [Thu Nov 01 2018 15:00:00 GMT-0400 (Eastern Daylight Time), Mon Nov 05 2018 15:00:00 GMT-0500 (Eastern Standard Time), Wed Nov 07 2018 15:00:00 GMT-0500 (Eastern Standard Time), Thu Nov 08 2018 15:00:00 GMT-0500 (Eastern Standard Time), Mon Nov 12 2018 15:00:00 GMT-0500 (Eastern Standard Time), Wed Nov 14 2018 15:00:00 GMT-0500 (Eastern Standard Time), Thu Nov 15 2018 15:00:00 GMT-0500 (Eastern Standard Time)]

Aber ich möchte, dass das Ergebnis America/Denver Zeit darstellt, nicht meine Systemzeitzone oder echte UTC. Wie kann ich die Zone auf America/Denver umstellen? Ich bin mit Luxon nicht vertraut, also habe ich stattdessen moment() versucht:

RRule.fromString(
    "DTSTART;TZID=America/Denver:20181101T130000;\n"
    + "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=7"
).all().map(d => moment(new Date(
    d.getUTCFullYear(),
    d.getUTCMonth(),
    d.getUTCDate(),
    d.getUTCHours(),
    d.getUTCMinutes(),
    d.getUTCSeconds()
)).tz('America/Denver').toDate());

Aber dies gibt dasselbe zurück, während es in New Yorker Zeit ist, weil toDate() es sofort wieder in die Systemzeitzone zurückversetzt (zumindest in Chrome).

@shorlbeck Wenn Sie bemerken, dass sowohl das Ergebnis in MDT als auch in EDT in UTC gleich sind, lautet meine Frage: Sind Sie sicher, dass Sie den Parameter TZID verwenden möchten und nicht nur die Gleitzeit? Ich empfehle Floating Time (im Grunde die gleiche, aber mit DSTART:<datetime> statt DSTART;TZID=<timezone>:<datetime> ), wenn Sie möchten, dass unabhängig von der Systemzeitzone immer 13:00 generiert wird.

13:00 Pseudo-UTC also. Wenn Sie das Chrome-Problem umgehen möchten, müssen Sie immer noch in ein lokales Datum konvertieren, indem Sie die Methode in meinem vorherigen Kommentar verwenden.

@davidgoli Oh wow, du hast Recht. Ich wusste nicht, dass ich den tzid-Parameter eigentlich nicht brauche, nachdem ich verstanden hatte, wie Floating Time/Pseudo-UTC tatsächlich funktioniert. Und das bedeutet, dass ich die obige .map() -Lösung auch nicht brauche. Dein Kommentar hat mich in die richtige Richtung geführt. Vielen Dank.

Ich würde lügen, wenn ich sagen würde, dass dies nicht die verwirrendste Sache war, an der ich seit langem gearbeitet habe, aber ich bin einfach froh, jetzt in die richtige Richtung zu gehen.

Haben Sie darüber nachgedacht, eine explizite Darstellung dessen zu verwenden, was Sie benötigen (gleitende Daten ohne Zeitzone)? Die Verwendung von nativem JS-Datum mit UTC-Zeitzone und die Behauptung, es sei UTC in den Dokumenten, ist äußerst verwirrend, da es irgendwie funktioniert, bis es Sie mit DST-Fehlern trifft.

Ich würde ein Objekt vorschlagen, nennen wir es "RRuleDate", das einen (year, month, day, hours?, minutes?, seconds?, milliseconds?) -Konstruktor verwendet und eine .toDate() -Methode hat.

Dieses RRuleDate ist das, was before() , after() , between() und all() zurückgeben sollten, und das einzige, was before() , after() , between() und die Option dtstart sollten als Parameter akzeptiert werden.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen