Rrule: Le changement d'heure d'été n'est pas géré correctement malgré la "prise en charge du fuseau horaire"

Créé le 20 nov. 2018  ·  21Commentaires  ·  Source: jakubroztocil/rrule

J'ai comparé le comportement de cette bibliothèque (v2.5.6) sur le front-end à la bibliothèque PHP équivalente (v2.3.3) sur le back-end. Il y a une incohérence certaine dans la façon dont le changement d'heure d'été est géré, et je pense que la version PHP le gère mieux.

Exemple de code :

Dans le fuseau horaire America/Denver , un passage à l'heure d'été a lieu le dimanche 4 novembre 2018 (passant de GMT-6 à GMT-7). Configurons donc une série récurrente commençant à 13h, se répétant tous les lundis, mercredis, jeudis, à partir du 1er novembre qui est avant le changement d'heure :

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

Dans ce cas, je m'attendrais à ce que toutes les récurrences commencent à 13h00 comme ci-dessous. (Selon les documents, j'utilise la bibliothèque Luxon. Je n'ai rien vu dire que je devais l'initialiser d'une manière ou d'une autre, donc je suppose qu'il est "installé" correctement via le fil.)

Résultat attendu (tous à partir de 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)
]

Résultat actuel:

(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
]

Lorsque j'ai configuré une situation similaire dans la bibliothèque PHP, le résultat était comme prévu ci-dessus, avec toutes les instances commençant à 13:00:00 dans le fuseau horaire America/Denver, pas UTC .

Autres détails:

  • Variante 2.5.6
  • Mac OS X 10.13.6
  • Chrome 70
  • Mon heure locale actuelle est MST (Amérique/Denver, GMT-7)

Edit : Correction d'une faute de frappe

Tous les 21 commentaires

Recevez-vous des avertissements sur la console ? par exemple:

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

Luxon est dans optionalDependencies , il sera donc importé s'il se trouve dans votre node_modules mais sinon non.

Lorsque j'exécute votre code dans la suite de tests rrule, j'obtiens :

      [
        [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]
      ]

ce à quoi je m'attendais, puisque l'heure que vous avez demandée est 1900 dans le fuseau horaire de Denver. (Puisqu'il s'agit de JavaScript et non de PHP, nous ne pouvons pas vous donner d'objets Date dans un fuseau horaire autre que le fuseau horaire de votre machine locale ou UTC, donc UTC est ce que cette bibliothèque utilise à tout moment.)

Le fait que vous voyiez des dates locales (MST/MDT) et non des dates UTC me déconcerte un peu. Autant que je sache, cette bibliothèque est actuellement configurée pour ne renvoyer que des dates UTC, bien qu'elle renvoyait auparavant des dates locales à la place.

Recevez-vous des avertissements sur la console ? par exemple:

'L'utilisation de TZID sans Luxon disponible n'est pas prise en charge. Les heures renvoyées sont en UTC, pas le fuseau horaire demandé'

Aucun avertissement de la console. Il semble que Luxon soit fourni correctement.

En ce qui concerne les fuseaux horaires, j'obtiens MST/DST suite à l'exécution de mon code directement dans la ligne de commande de la console devtools Chrome.

Étapes : Ouvrez Chrome --> Naviguez vers la page avec RRule et Luxon fournis --> Cmd+Opt+I --> Onglet "Console" --> copiez et collez l'extrait ci-dessus dans la ligne de commande en bas --> Entrez

cette bibliothèque est actuellement configurée pour ne renvoyer que des dates UTC, bien qu'elle renvoyait auparavant des dates locales à la place.

Savez-vous à quelle version cela a changé ? Pourquoi ce changement serait-il fait? Le fait de ne renvoyer que les dates UTC introduit en fait cet écart d'heure d'été ! Un événement qui se produit toujours à 13h00 UTC garantit à peu près que l'événement commencera à une heure différente avant/après un passage à l'heure d'été. Je pense que ce changement devrait être annulé, ou bien une autre méthode alternative devrait être fournie qui permette de prendre en compte l'heure d'été. UTC est extrêmement utile mais ce n'est pas toujours la bonne approche dans toutes les situations. Lorsque vous travaillez avec des événements récurrents, nous DEVONS prendre en compte le fuseau horaire spécifique dans lequel l'événement se produit, en particulier parce que l'heure d'été est une chose.

En effet, j'obtiens un comportement différent de Chrome que de Node.

Je dirais que je ne comprends pas pourquoi votre sortie "attendue" est ce que vous attendez vraiment. Ces heures semblent incorrectes.

Ce qui _serait_ correct, ce sont les heures si vous ajoutiez le décalage TZ pour chaque heure à l'heure locale donnée. Ensuite, tous les horaires seront 19h00. C'est pourquoi RRule traite de l'UTC, et pourquoi son utilisation de l'UTC n'a rien à voir avec ce bogue particulier.

En effet, si vous faites :

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

vous verrez les chaînes ISO résultantes avec systématiquement les dates et heures correctes.

Il semble, de plus, que Chrome ne supporte pas du tout le retour des dates en UTC :

> 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 utilise Luxon pour générer l'heure locale correcte pour _tous les fuseaux horaires du monde_, pas seulement votre fuseau horaire local. C'est pourquoi son utilisation de l'UTC est critique : si le code de fuseau horaire renvoyait des dates "correctes" pour votre fuseau horaire local, mais que vous deviez générer des dates dans un fuseau horaire différent (donné avec le TZID ), vous obtiendriez des résultats totalement incorrects. Nous utilisons des "dates UTC" non pas parce que nous faisons des calculs DST en UTC (nous ne le sommes pas, vous pouvez inspecter la suite de tests et voir de nombreux exemples de calculs DST corrects), mais parce que les fuseaux horaires dans la spécification JavaScript sont horriblement cassé, et UTC est le plus proche d'un concept de date "neutre" disponible. Je suis désolé qu'il soit déroutant de travailler avec et que l'utilisation de cette bibliothèque ne puisse pas être plus claire (bien que je sois ouvert aux suggestions).

Ce qui _est_ préoccupant, c'est le problème de compatibilité du navigateur que vous soulevez. Je vous demanderais maintenant d'essayer votre propre exemple de code dans une console Node en cours d'exécution, ou dans Firefox d'ailleurs. Vous verrez des résultats très différents (et corrects).

Donc, le vrai problème ici est que les résultats sont déroutants dans Chrome. Je pense que cela vaut la peine d'être abordé, bien que je ne sache pas exactement comment le faire. Ce que je _peux_ vous dire, c'est que les deux environnements génèrent exactement les mêmes chaînes ISO et horodatages.

@davidgoli Vous avez raison, Firefox affiche un résultat différent dans la console lors de l'exécution de mon code d'origine. Cependant, le résultat est finalement le même temps, c'est juste que le résultat est exprimé en heure UTC dans Firefox alors que dans Chrome, il est exprimé en heure America/Denver (ou probablement quel que soit votre fuseau horaire local).

Je dirais que je ne comprends pas pourquoi votre sortie "attendue" est ce que vous attendez vraiment. Ces heures semblent incorrectes.

Ce qui serait correct, ce sont les heures si vous ajoutiez le décalage TZ pour chaque heure à l'heure locale donnée. Ensuite, tous les horaires seront 19h00. C'est pourquoi RRule traite de l'UTC, et pourquoi son utilisation de l'UTC n'a rien à voir avec ce bogue particulier.

Permettez-moi d'essayer d'expliquer pourquoi nous ne voulons PAS de 19h00 UTC pour chaque événement récurrent.

Supposons qu'un utilisateur souhaite planifier un événement récurrent à un emplacement spécifique quelque part dans le fuseau horaire America/Denver pour chaque mercredi à 13 h 00. Autrement dit, cela signifie - quelle que soit la période de l'année - que l'utilisateur s'attend à ce que ce même événement se produise tous les mercredis à 13 heures chaque fois que c'est mercredi à 13 heures à cet endroit . Pas 12h, pas 14h. Toujours à 13h pour l'heure Amérique/Denver.

Cependant, rrule résout les récurrences en fonction de l'heure UTC - même lorsqu'un fuseau horaire est transmis. En faisant cela, il dit essentiellement : "Oh, 13h00 Amérique/Denver est évalué à 19h00. Par conséquent, 19h00 doit TOUJOURS se référer à 13h00 en Amérique/Denver, toute l'année. " Mais ce n'est tout simplement pas vrai. Avant le 4 novembre 2018, c'est vrai, mais après le 4 novembre, America/Denver passe à l'heure d'été, à ce moment-là, 19h00 UTC correspond maintenant à 12h00 America/Denver time . Ainsi, notre utilisateur est informé, à tort, que l'événement se produit à 12h et non à 13h.

En d'autres termes, 1pm America/Denver peut être 19:00 ou 20:00 UTC selon que nous sommes à l'heure d'été ou non, et il est impossible d'utiliser rrule pour obtenir les bonnes récurrences car il veut dire que 19 : 00 est la bonne heure toute l'année.

J'ai l'impression que rrule s'inscrit dans le mythe auquel croient la plupart des programmeurs, à savoir que UTC est toujours la bonne solution lorsqu'il s'agit de dates et d'heures. Mais il y a certains cas où ce n'est pas la meilleure solution, et ce cas (répéter des événements futurs dans un endroit spécifique) en fait partie.


Edit: En théorie, je suis d'accord avec la règle renvoyant les heures en UTC, mais ces heures devraient être correctes pour le fuseau horaire donné, lorsqu'un fuseau horaire est transmis. Cela signifie que nous devrions voir 19h00 UTC passer à 20h00 UTC à un certain point dans le jeu de résultats, pour l'exemple discuté ici.

Laissez-moi revenir ici - je pense que c'est un problème de documentation.

RRule renvoie _toujours_ les dates JS "UTC". Cela ne signifie pas, cependant, que ces dates sont censées représenter l'heure UTC. Au lieu de cela, en raison des limitations de la seule alternative - les dates locales - la décision a été prise que _seulement_ les dates JS avec un décalage de fuseau horaire de 0 sont utilisables pour les calculs de date effectués par cette bibliothèque. Cela a pour effet secondaire trompeur qu'il semble que ces heures représentent en fait l'heure en UTC, alors qu'en fait elles sont censées être interprétées dans le fuseau horaire représenté par le paramètre TZID . Ainsi, par exemple, dans Firefox, le comportement prévu est que la bibliothèque vous renvoie une date représentant 19h00 et, comme vous avez demandé que cette heure soit l'heure de Denver, elle sera correcte pour l'heure de Denver. Vous êtes censé ignorer le fait que cette date indique qu'il s'agit d'une date UTC, car c'est le seul type de date utile en JavaScript. Au lieu de cela, vous êtes censé utiliser la connaissance que cette règle a le fuseau horaire de Denver pour interpréter 19h00 comme étant l'heure locale à Denver.

Ceci est plus clair lorsque vous utilisez des fuseaux horaires en dehors de la zone de votre machine locale. Par exemple, si vous configurez votre RRule avec le nom de zone America/New_York la place, vous verrez que les dates sont renvoyées sous la forme 17:00, car il s'agit de l'heure locale dans le fuseau horaire de votre machine (Denver) lorsqu'il est 19 :00 dans le fuseau horaire demandé (New York).

JavaScript n'a aucun moyen "d'épingler" une date à un fuseau horaire spécifique, donc nous représentons toujours la date et l'heure correctes quoi qu'il arrive, et vous pouvez ignorer le fait que la date se signale comme UTC. Lorsque des fuseaux horaires sont utilisés dans RRule, il s'agit en fait d'une heure locale. C'est pourquoi Firefox vous donne l'heure locale correcte à 19h00 (même si cette date est signalée comme UTC, c'est en fait l'heure de Denver).

Est-ce que cela clarifie les choses ? Je comprends que c'est déroutant et pas intuitif, mais j'espère que c'est assez simple pour comprendre qu'une fois compris, il devient facile de raisonner. Nous devrions certainement mieux documenter cela.

Une chose que j'aimerais explorer à partir de cela est la possibilité d'utiliser Luxon pour rezoner les dates résultantes dans le fuseau horaire local, donc c'est au moins conforme à vos attentes. Je pense que cela ne devrait pas être trop difficile et devrait aider à clarifier des malentendus comme celui-ci. Le problème de Chrome est vraiment nouveau pour moi, et c'est une mauvaise nouvelle.

Ce serait un changement radical, cependant, je voudrais donc offrir la possibilité aux commentaires de la communauté avant l'expédition.

@shorlbeck Ce diff, je crois, implémente le comportement que vous attendez:

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(),
+    ])
+  })
 })

Cependant, je ne suis pas entièrement convaincu que ce soit la voie à suivre pour cette bibliothèque. étant donné l'approche générale de la bibliothèque consistant à renvoyer des heures "flottantes" qui sont toujours destinées à être interprétées dans le fuseau horaire local du client (bien qu'avec un décalage de 0). Le getter approprié pour les dates renvoyées par rrule sont les getters getUTC* , donc par exemple, si vous utilisez getUTCHours() à chaque date dans votre résultat d'origine, vous obtiendrez le heures correctes, même dans Chrome.

Je penche pour penser que la solution correcte ici n'est pas de changer le comportement de cette bibliothèque, mais de documenter plus clairement son utilisation quelque peu idiosyncratique du pseudo-"UTC" (en fait "flottant"). Je suis ouvert à la persuasion à ce sujet, cependant.

J'ai mis à jour le README avec quelques clarifications et instructions pour votre cas d'utilisation. Cependant, je suis intéressé à poursuivre cette discussion et ouvert à la possibilité de modifier ce comportement dans une future version de la bibliothèque.

Merci pour la réponse et l'explication détaillée.

Malheureusement, je n'ai jamais été aussi confus.

Travailler avec des dates et des heures est déjà assez difficile lorsque ces dates sont en fait UTC ou en fait une heure locale, mais devoir également se rappeler que parfois une heure UTC n'est pas réellement une heure UTC est plus que ce que mon cerveau peut gérer. J'ai passé toute la journée d'aujourd'hui à essayer de comprendre ce que vous avez écrit, mais en fin de compte, c'est tellement contre-intuitif que je n'arrive pas à comprendre.

Chaque fois que je regarde le code et que je vois une date "UTC", je ne me souviens pas s'il s'agit réellement d'UTC ou simplement d'un pseudo-UTC , en particulier lorsque vous y ajoutez le problème Chrome. Cela rend presque impossible pour moi de coder. Cela ruine ma confiance dans la lisibilité de mon code.

Voici ma question, alors... Pour en revenir à mon exemple d'origine... En vrai UTC, 2018-11-01 19:00:00 fait référence à 13h00 en Amérique/Denver (13h00). Et dans mon exemple, 1pm America/Denver est l'heure correcte de mon événement. Mais si l'UTC n'est pas le vrai UTC, cela signifie-t-il que je devrais passer new Date(Date.UTC(2018, 10, 1, 13, 0, 0)) ou new Date(Date.UTC(2018, 10, 1, 19, 0, 0)) lors de l'utilisation du tzid ? (La différence étant 13h00 vs 19h00)

Est-ce que le pseudo-UTC entre et sort ? Ou le vrai UTC entrant mais le pseudo-UTC sortant ? Et comment puis-je m'en souvenir de manière fiable ? ?

Votre règle est spécifiée comme suit :

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

Mais selon la RRULE RFC5545, cela indique une règle dans le fuseau horaire de Denver avec l'heure locale de 1900 : 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

Si vous voulez un DTSTART à 1300 heure locale de Denver, vous devez le spécifier comme 1300 dans votre règle. La bibliothèque doit être parfaitement conforme à la spécification à cet égard. (Eh bien, à part le fait mineur qu'il reviendra à 1300 UTC ...)

De plus, assurez-vous de lire la section sur le "temps flottant":

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, malheureusement, n'offre pas d'implémentation du "temps flottant". L'option la plus proche que nous ayons d'une heure "pure", sans fuseau horaire, est l'UTC, d'où "psuedo-UTC". (Pour mémoire, rrule prend également en charge les temps flottants, spécifiés sans TZID ni indicatif UTC Z .)

Le passage à UTC a été entièrement commencé avec ce commit : https://github.com/jakubroztocil/rrule/commit/850ed075175eb1acfcbd7b2cddf0606f2b2206f7

Si vous consultez le commit précédent et sélectionnez le test ajouté à partir de # 850ed075, vous le verrez échouer. En fait, des centaines de tests échouaient à moins que toute la suite ne soit exécutée en UTC. En effet, afin de calculer les dates futures qui traversent les limites de l'heure d'été, nous avons dû instancier les dates lorsque l'horloge actuelle fonctionnait dans l'heure d'été, puis calculer les mathématiques qui nous ont mis à l'heure standard, ce qui a entraîné un décalage incorrect d'une heure. L'implémentation actuelle donne toujours des heures locales correctes. Désormais, l'ensemble de la suite de tests passera en fait dans n'importe quel environnement local, avec la mise en garde que pour obtenir la date/heure correcte, vous devez utiliser les méthodes getUTCDate() / getUTCHour() .

J'étudie la possibilité de modifier cela dans une future version, mais le changement n'est pas anodin sans l'adoption d'une bibliothèque tierce requise comme Luxon.

Dans une prochaine version, j'aimerais changer le comportement pour renvoyer des dates toujours correctes dans votre fuseau horaire local, par exemple :

  • DTSTART;TZID=America/Denver:20181101T130000 vous renverrait une date qui est 1300-0700 (heure standard) si vous êtes à Denver, ou 2000Z si vous êtes en UTC
  • DTSTART=20181101T130000 vous donnerait 1300-0700 si vous êtes à Denver, et 1300Z si vous êtes en UTC
  • DTSTART=20181101T130000Z vous donnerait 0600-0700 si vous êtes à Denver, et 1300Z si vous êtes en UTC

Est-ce le comportement que vous attendez ?

Un gros problème est qu'un environnement JS local donné ne peut représenter que des dates dans un seul fuseau horaire. Ainsi, ni new Date(...) ni new Date(Date.UTC(...)) ne sont vraiment appropriés lorsque vous souhaitez représenter une date-heure dans un fuseau horaire spécifique (qui peut être différent de votre fuseau horaire actuel).

Alors, quel est le comportement supposé être avec :

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

si la machine locale est dans America/Los_Angeles ? Vous devez être au moins quelque peu conscient de la différence d'une heure entre l'heure que vous créez et le fuseau horaire dans lequel vous souhaitez l'utiliser. Est-il donc censé représenter 11h (heure de Denver, le fuseau demandé) ou 10h ( heure de Los Angeles, la zone d'origine) ? Je pense que vous pourriez plaider l'un ou l'autre.

De manière pédante, un objet Date est différent à la fois d'une chaîne de date ISO et d'une chaîne DTSTART, en ce qu'il représente un horodatage en millisecondes UTC qui n'a aucun concept de fuseau horaire ou de décalage. Je pense donc qu'il est plus logique de représenter l'horodatage de l'heure souhaitée _dans la bonne zone_ ce qui signifie que si vous êtes à Los Angeles et que vous voulez représenter l'heure à Denver, vous devez passer un objet Date initialisé avec le relatif décalage appliqué (ainsi l'exemple ci-dessus représenterait 11h à Denver). C'est déroutant cependant, car si vous voyagez ensuite à New York et que vous voulez représenter la même heure, vous devez écrire new Date(2018, 10, 1, 13, 0, 0) !

Un avantage à jeter tout le concept de rendez-vous locaux par la fenêtre est que vous n'avez pas à vous en occuper; la date et l'heure représentées peuvent être traitées comme une heure flottante, capable d'être projetée dans n'importe quel fuseau horaire sans faire aucune conversion.

Ceci est compliqué, bien sûr, par le comportement de moteurs comme Chrome.

Pour vos besoins, vous pouvez probablement obtenir les valeurs souhaitées en faisant :

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

J'essaie actuellement de décider si cela appartient à la bibliothèque proprement dite...

Merci! Votre réponse m'a vraiment clarifié les choses. Je pense que j'étais fatigué et épuisé hier alors que j'essayais de tout comprendre. Je vois maintenant ce qui se passe, et vous avez raison, je passais au mauvais moment en utilisant le tzid .

Pour vos besoins, vous pouvez probablement obtenir les valeurs souhaitées en faisant :

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

J'essaie actuellement de décider si cela appartient à la bibliothèque proprement dite...

Cela a fonctionné pour moi! Merci beaucoup. Ce serait bien s'il y avait peut-être un moyen de configurer ce qui est renvoyé par rule.all() et rule.between() afin que nous n'introduisions pas encore un autre changement de rupture, mais que nous puissions toujours obtenir le comportement ci-dessus de manière native. Vous ne savez pas comment accomplir cela lorsque vous considérez également que j'aime utiliser rrulestr() ou RRule.fromString() mais peut-être un argument supplémentaire ou quelque chose pour désigner le type de date renvoyé ?


MISE À JOUR : Le d.getUTCMilliseconds() ci-dessus devrait être remplacé par d.getUTCSeconds()

Ainsi, votre solution fonctionne lorsque le fuseau horaire de mon système est le même que le fuseau horaire demandé, mais j'ai du mal à comprendre comment faire en sorte que RRule renvoie les dates dans le fuseau horaire demandé lorsqu'il diffère du fuseau horaire du système. Il renvoie toujours les dates représentées dans le fuseau horaire du système, au lieu d'être représentées dans le fuseau horaire demandé. Exemple:

  1. Définissez le fuseau horaire de votre système d'exploitation sur America/Denver
  2. Dans la console des outils de développement Chrome, saisissez :
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()
));

Le résultat est correct (pour ce que j'attends/besoin):

(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. Changez le fuseau horaire du système d'exploitation en America/New_York
  2. Exécutez la même commande dans la console devtools de Chrome.

Le résultat diffère, car il est représenté dans le fuseau horaire du système :

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

Mais je veux que le résultat représente l'heure America/Denver , pas mon fuseau horaire système, ni le vrai UTC. Comment puis-je le rezoner pour qu'il soit America/Denver ? Je ne connais pas Luxon, j'ai donc essayé moment() la place :

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

Mais cela renvoie la même chose à l'heure de New York, car toDate() le remet instantanément dans le fuseau horaire du système (dans Chrome, au moins).

@shorlbeck Si vous remarquez que le résultat en MDT et en EDT sont les mêmes en UTC, ma question est donc la suivante: êtes-vous sûr de vouloir utiliser le TZID et pas seulement le temps flottant? Je recommande le temps flottant (essentiellement le même, mais avec DSTART:<datetime> au lieu de DSTART;TZID=<timezone>:<datetime> ) si vous voulez qu'il génère toujours 13h00 quel que soit le fuseau horaire du système.

13:00 pseudo-UTC c'est-à-dire. Si vous souhaitez contourner le problème de Chrome, vous devrez toujours convertir en une date locale en utilisant la méthode de mon commentaire précédent.

@davidgoli Oh wow, tu as raison. Je n'avais pas réalisé que je n'avais pas réellement besoin du paramètre tzid après avoir compris comment le temps flottant/pseudo-UTC fonctionnait réellement. Et cela signifie que je n'ai pas non plus besoin de la solution .map() ci-dessus. Votre commentaire m'a orienté dans la bonne direction. Merci beaucoup.

Je mentirais si je disais que ce n'était pas la chose la plus déroutante sur laquelle j'ai travaillé depuis longtemps, mais je suis juste content d'être dans la bonne direction maintenant.

Avez-vous pensé à utiliser une représentation explicite de ce dont vous avez besoin (dates flottantes sans fuseau horaire) ? Utiliser la date JS native avec le fuseau horaire UTC et prétendre qu'il s'agit de l'UTC dans la documentation est extrêmement déroutant car cela fonctionne en quelque sorte jusqu'à ce qu'il vous frappe avec des bogues DST.

Je suggérerais un objet, appelons-le "RRuleDate" qui prend un constructeur (year, month, day, hours?, minutes?, seconds?, milliseconds?) et a une méthode .toDate() .

Ce RRuleDate est ce que before() , after() , between() et all() devraient retourner, et la seule chose que before() , after() , between() et l'option dtstart doivent être acceptées comme paramètres.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

anthwinter picture anthwinter  ·  11Commentaires

fatshotty picture fatshotty  ·  5Commentaires

spurreiter picture spurreiter  ·  3Commentaires

agordeev picture agordeev  ·  16Commentaires

zeluspudding picture zeluspudding  ·  11Commentaires