Rrule: El cambio de horario de verano no se manejó correctamente a pesar de "Soporte de zona horaria"

Creado en 20 nov. 2018  ·  21Comentarios  ·  Fuente: jakubroztocil/rrule

He estado comparando el comportamiento de esta biblioteca (v2.5.6) en el front-end con la biblioteca PHP equivalente (v2.3.3) en el back-end. Hay una clara inconsistencia con la forma en que se maneja el cambio de horario de verano, y creo que la versión de PHP lo maneja mejor.

Ejemplo de código:

En la zona horaria America/Denver , se produce un cambio de horario de verano el domingo 4 de noviembre de 2018 (pasando de GMT-6 a GMT-7). Entonces, configuremos una serie recurrente que comience a la 1 p. m., repitiendo todos los lunes, miércoles y jueves, comenzando el 1 de noviembre, que es antes del cambio de horario:

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

En este caso, esperaría que todas las recurrencias comenzaran a la 1:00 p. m., como se muestra a continuación. (Según los documentos, estoy usando la biblioteca Luxon. No vi nada que dijera que tenía que inicializarlo de alguna manera, así que asumo que está "instalado" correctamente a través de yarn).

Resultado esperado (todos a partir de las 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)
]

Resultado actual:

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

Cuando configuré una situación similar en la biblioteca de PHP, el resultado fue el esperado anteriormente, con todas las instancias comenzando a las 13:00:00 en la zona horaria de América/Denver, no UTC .

Otros detalles:

  • Versión 2.5.6
  • Mac OS X 10.13.6
  • cromo 70
  • Mi hora local actual es MST (América/Denver, GMT-7)

Editar: corregir error tipográfico

Todos 21 comentarios

¿Recibes alguna advertencia en la consola? p.ej:

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

Luxon está en optionalDependencies , por lo que se importará si está en su node_modules pero no en caso contrario.

Cuando ejecuto su código en el conjunto de pruebas rrule, obtengo:

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

que es lo que esperaría, ya que la hora que solicitó es 1900 en la zona horaria de Denver. (Dado que se trata de JavaScript y no de PHP, no podemos darle Date objetos en una zona horaria que no sea la zona horaria de su máquina local o UTC, por lo que esta biblioteca usa UTC en todo momento).

El hecho de que estés viendo fechas locales (MST/MDT) y no fechas UTC es un poco desconcertante para mí. Hasta donde yo sé, esta biblioteca actualmente está configurada para devolver solo fechas UTC, aunque anteriormente solía devolver fechas locales.

¿Recibes alguna advertencia en la consola? p.ej:

'No se admite el uso de TZID sin Luxon disponible. Los tiempos devueltos están en UTC, no en la zona horaria solicitada.

No hay advertencias en la consola. Parece que Luxon se proporciona correctamente.

En cuanto a las zonas horarias, obtengo MST/DST como resultado de ejecutar mi código directamente en la línea de comando de la consola Chrome devtools.

Pasos: Abra Chrome --> Navegue a la página con RRule y Luxon provistos --> Cmd+Opt+I --> pestaña "Consola" --> copie y pegue el fragmento anterior en la línea de comando en la parte inferior --> Enter

esta biblioteca actualmente está configurada para devolver solo fechas UTC, aunque anteriormente solía devolver fechas locales en su lugar.

¿Sabes en qué versión cambió esto? ¿Por qué se haría ese cambio? ¡Devolver solo las fechas UTC en realidad introduce esta discrepancia de horario de verano! Un evento que siempre ocurre a las 13:00 UTC prácticamente garantiza que el evento comenzará a una hora diferente antes o después del cambio de horario de verano. Creo que se debe revertir este cambio, o bien se debe brindar otro método alternativo que permita contabilizar el horario de verano. UTC es extremadamente útil, pero no siempre es el enfoque correcto en todas las situaciones. Cuando trabajamos con eventos recurrentes, TENEMOS que tener en cuenta la zona horaria específica en la que ocurre el evento, específicamente porque existe el horario de verano.

De hecho, obtengo un comportamiento diferente de Chrome que de Node.

Diría que no entiendo por qué su resultado "esperado" dado es lo que realmente espera. Esos tiempos parecen incorrectos.

Lo que _sería_ correcto son los tiempos si agregara el desplazamiento TZ para cada tiempo a la hora local dada. Entonces todos los horarios serán las 19:00. Esta es la razón por la que RRule trata con UTC y por qué su uso de UTC no tiene nada que ver con este error en particular.

De hecho, si lo haces:

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

verá las cadenas ISO resultantes con las fechas y horas correctas de manera constante.

Además, parece que Chrome no admite la devolución de fechas en UTC en absoluto:

> 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 usa Luxon para generar la hora local correcta para _todas las zonas horarias del mundo_, no solo para su zona horaria local. Esta es la razón por la que su uso de UTC es fundamental: si el código de la zona horaria devuelve fechas "correctas" para su zona horaria local, pero necesita generar fechas en una zona horaria diferente (dada con el TZID ), obtendría resultados tremendamente incorrectos. Estamos usando "fechas UTC" no porque estemos haciendo matemáticas de DST en UTC (no lo estamos, puede inspeccionar el conjunto de pruebas y ver muchos ejemplos de cálculos correctos de DST), sino porque las zonas horarias en la especificación de JavaScript son horriblemente roto, y UTC es lo más cercano a un concepto de fecha "neutral" que está disponible. Lamento que sea confuso trabajar con él y que el uso de esta biblioteca no pueda ser más claro (aunque estoy abierto a sugerencias).

Lo que _es_ preocupante es el problema de compatibilidad del navegador que mencionas. Le pediría que ahora pruebe su propio código de muestra en una consola Node en ejecución, o en Firefox para el caso. Verá resultados muy diferentes (y correctos).

Entonces, el verdadero problema aquí es que los resultados son confusos en Chrome. Creo que vale la pena abordarlo, aunque no me queda claro inmediatamente cómo hacerlo. Lo que _puedo_ decirte es que ambos entornos generan exactamente las mismas cadenas ISO y marcas de tiempo.

@davidgoli Tienes razón, Firefox muestra un resultado diferente en la consola cuando ejecuta mi código original. Sin embargo, el resultado es, en última instancia, la misma hora, solo que el resultado se expresa en la hora UTC en Firefox, mientras que en Chrome se expresa en la hora America/Denver (o probablemente en la zona horaria local).

Diría que no entiendo por qué su resultado "esperado" dado es lo que realmente espera. Esos tiempos parecen incorrectos.

Lo que sería correcto son los tiempos si agregara el desplazamiento TZ para cada tiempo a la hora local dada. Entonces todos los horarios serán las 19:00. Esta es la razón por la que RRule trata con UTC y por qué su uso de UTC no tiene nada que ver con este error en particular.

Permítanme intentar explicar por qué NO queremos las 19:00 UTC para cada evento recurrente.

Supongamos que un usuario desea programar un evento repetitivo en una ubicación específica en algún lugar de la zona horaria America/Denver para todos los miércoles a la 1 p. m. En pocas palabras, eso significa que, independientemente de la época del año , el usuario espera tener este mismo evento todos los miércoles a la 1 p. m. siempre que sea miércoles a la 1 p. m. en esa ubicación . Ni a las 12, ni a las 14. Siempre a la 1 p. m. para la hora de Estados Unidos/Denver.

Sin embargo, rrule está resolviendo las recurrencias de acuerdo con la hora UTC, incluso cuando se pasa una zona horaria. Al hacer esto, básicamente dice: "Oh, 1:00 p. m. América/Denver se evalúa como 19:00. Por lo tanto, 19:00 SIEMPRE debe referirse a la 1:00 p. m. en Estados Unidos/Denver, durante todo el año". Pero eso no es cierto. Antes del 4 de noviembre de 2018, eso es cierto, pero después del 4 de noviembre, América/Denver deja de funcionar según el horario de verano, momento en el que las 19:00 UTC ahora se evalúan como las 12:00 p. m., hora de América/Denver . Entonces, a nuestro usuario se le dice, incorrectamente, que el evento está ocurriendo a las 12:00 p. m., no a la 1:00 p. m.

En otras palabras, la 1 p. m. América/Denver puede ser 19:00 o 20:00 UTC dependiendo de si estamos en horario de verano o no, y es imposible usar rrule para obtener las recurrencias correctas porque quiere decir que 19: 00 es la hora correcta durante todo el año.

Siento que rrule está comprando el mito que creen la mayoría de los programadores, que es que UTC siempre es la solución correcta cuando se trata de fechas y horas. Pero hay ciertos casos en los que no es la mejor solución, y este caso (repetir eventos futuros en una ubicación específica) es uno de ellos.


Editar: en teoría, estoy de acuerdo con que la regla devuelva los tiempos en UTC, pero esos tiempos deberían ser correctos para la zona horaria dada, cuando se pasa una zona horaria. Eso significa que deberíamos ver 19:00 UTC cambiar a 20:00 UTC en algún punto en el conjunto de resultados, para el ejemplo que se analiza aquí.

Déjame volver aquí. Creo que se trata de un problema de documentación.

RRule _siempre_ devuelve fechas JS "UTC". Sin embargo, esto no significa que esas fechas estén destinadas a representar la hora UTC. Más bien, debido a las limitaciones de la única alternativa, las fechas locales, se tomó la decisión de que _solo_ las fechas JS con 0 desplazamiento de zona horaria se pueden usar para la matemática de fechas que está haciendo esta biblioteca. Esto tiene el efecto secundario engañoso de que parece que estos tiempos en realidad representan el tiempo en UTC, cuando en realidad están destinados a ser interpretados en la zona horaria representada por el parámetro TZID . Entonces, por ejemplo, en Firefox, el comportamiento previsto es que la biblioteca le devuelva una Fecha que representa las 19:00 y, dado que solicitó que esa hora fuera la hora de Denver, será correcta para la hora de Denver. Debe ignorar el hecho de que esta fecha informa que es una fecha UTC, porque ese es el único tipo de fecha que es útil en JavaScript. En su lugar, debe utilizar el conocimiento de que esta regla tiene la zona horaria de Denver para interpretar las 19:00 como la hora local de Denver.

Esto es más claro cuando usa zonas horarias fuera de la zona de su máquina local. Por ejemplo, si configura su RRule con el nombre de zona America/New_York , verá que las fechas se devuelven como 17:00, porque esta es la hora local en la zona horaria de su máquina (Denver) cuando son las 19 :00 en la zona horaria solicitada (Nueva York).

JavaScript no tiene forma de "fijar" una fecha a una zona horaria específica, por lo que siempre representamos la fecha y la hora correctas sin importar qué, y puede descartar el hecho de que la Fecha se informa a sí misma como UTC. Cuando se utilizan zonas horarias en RRule, es, de hecho, una hora local. Esta es la razón por la que Firefox le da la hora local correcta a las 19:00 (aunque esa fecha se informa como UTC, en realidad es la hora de Denver).

¿Esto aclara las cosas en absoluto? Entiendo que es confuso y no intuitivo, pero espero que sea lo suficientemente simple como para comprender que una vez que se entiende, se vuelve fácil razonar. Definitivamente deberíamos documentar esto mejor.

Una cosa que me gustaría explorar a partir de esto es la posibilidad de usar Luxon para rezonificar las fechas resultantes en la zona horaria local, por lo que al menos es consistente con sus expectativas. Creo que esto no debería ser demasiado difícil y debería ayudar a aclarar malentendidos como este. El problema de Chrome es realmente nuevo para mí y son malas noticias.

Sin embargo, este sería un cambio importante, por lo que me gustaría ofrecer la oportunidad de recibir comentarios de la comunidad antes del envío.

@shorlbeck Esta diferencia, creo, implementa el comportamiento que espera ver:

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

Sin embargo, no estoy completamente convencido de que este sea el camino a seguir para esta biblioteca. dado el enfoque general de la biblioteca de devolver tiempos "flotantes" que siempre están destinados a ser interpretados en la zona horaria local del cliente (aunque con 0 de compensación). Los captadores apropiados para las fechas devueltas por rrule son los captadores getUTC* , por lo que, por ejemplo, si usa getUTCHours() en cada fecha en su resultado original, obtendrá el horas correctas, incluso en Chrome.

Me inclino a pensar que la solución correcta aquí no es cambiar el comportamiento de esta biblioteca, sino documentar su uso un tanto idiosincrático de tiempos pseudo-"UTC" (en realidad "flotantes") con mayor claridad. Estoy abierto a la persuasión sobre esto, sin embargo.

Actualicé el LÉAME con algunas aclaraciones e instrucciones para su caso de uso. Sin embargo, estoy interesado en continuar esta discusión y estoy abierto a posiblemente cambiar este comportamiento en una versión futura de la biblioteca.

Gracias por la respuesta y la explicación detallada.

Desafortunadamente, nunca he estado más confundido.

Trabajar con fechas y horas es bastante difícil cuando esas fechas y horas son en realidad UTC o en realidad una hora local, pero tener que recordar que a veces una hora UTC no es en realidad una hora UTC es más de lo que mi cerebro puede manejar. He pasado todo el día de hoy tratando de entender lo que escribiste, pero al final del día, es tan contradictorio que no puedo entenderlo.

Cada vez que miro el código y veo una fecha "UTC", no puedo recordar si es realmente UTC o simplemente pseudo-UTC , especialmente cuando agrega el problema de Chrome allí. Me está haciendo casi imposible codificar. Está arruinando mi confianza en la legibilidad de mi código.

Aquí está mi pregunta, entonces... Volviendo a mi ejemplo original... En UTC verdadero, 2018-11-01 19:00:00 se refiere a la 1 p. m. en hora de Estados Unidos/Denver (13:00). Y en mi ejemplo, la 1 p. m. América/Denver es la hora correcta de mi evento. Pero si el UTC no es UTC real, ¿significa eso que debo pasar new Date(Date.UTC(2018, 10, 1, 13, 0, 0)) o new Date(Date.UTC(2018, 10, 1, 19, 0, 0)) al usar el tzid ? (La diferencia es 13:00 vs. 19:00)

¿Es pseudo-UTC tanto al entrar como al salir? ¿O entra UTC verdadero pero sale pseudo-UTC? ¿Y cómo puedo recordar esto de manera confiable?

Su regla se especifica como:

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

Pero según la RRULE RFC5545, esto indica una regla en la zona horaria de Denver con la hora local 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 desea un DTSTART a las 1300 hora local de Denver, debe especificarlo como 1300 en su regla. La biblioteca debe ajustarse completamente a la especificación en ese sentido. (Bueno, excepto por el hecho menor de que regresará a las 1300 UTC...)

Además, asegúrese de leer la sección sobre "tiempo flotante":

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.

Desafortunadamente, JavaScript no ofrece una implementación de "tiempo flotante". La opción más cercana que tenemos a un tiempo "puro", sin zona horaria, es UTC, por lo tanto, "psuedo-UTC". (Para que conste, rrule también admite tiempos flotantes, especificados sin un TZID ni un designador UTC Z ).

El cambio a UTC comenzó por completo con este compromiso: https://github.com/jakubroztocil/rrule/commit/850ed075175eb1acfcbd7b2cddf0606f2b2206f7

Si revisa la confirmación anterior y selecciona la prueba agregada de # 850ed075, verá que falla. De hecho, cientos de pruebas fallaban a menos que todo el conjunto se ejecutara en UTC. Esto se debe a que, para calcular las fechas futuras que se encuentran a través de los límites del horario de verano, tuvimos que instanciar las fechas en las que el reloj actual se ejecutaba en el horario de verano y luego calcular las matemáticas que nos colocaron en la hora estándar, lo que resultó en un desplazamiento incorrecto de 1 hora. La implementación actual siempre da las horas locales correctas. Ahora, todo el conjunto de pruebas pasará de hecho en cualquier entorno local, con la advertencia de que para obtener la fecha/hora correcta debe usar los métodos getUTCDate() / getUTCHour() .

Estoy investigando cambiar esto en una versión futura, pero el cambio no es trivial sin adoptar una biblioteca de terceros requerida como Luxon.

En una próxima versión, me encantaría cambiar el comportamiento para devolver fechas que siempre sean correctas en su zona horaria local, por ejemplo:

  • DTSTART;TZID=America/Denver:20181101T130000 le devolvería una fecha que es 1300-0700 (hora estándar) si está en Denver, o 2000Z si está en UTC
  • DTSTART=20181101T130000 te daría 1300-0700 si estás en Denver, y 1300Z si estás en UTC
  • DTSTART=20181101T130000Z te daría 0600-0700 si estás en Denver, y 1300Z si estás en UTC

¿Es este el comportamiento que esperas?

Un gran problema es que un entorno JS local dado solo puede representar fechas en una sola zona horaria. Por lo tanto, ni new Date(...) ni new Date(Date.UTC(...)) son realmente apropiados cuando desea representar una fecha y hora en una zona horaria específica (que puede ser diferente de su zona horaria actual).

Entonces, ¿cuál se supone que es el comportamiento con:

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

si la máquina local está en America/Los_Angeles ? Tendría que ser al menos algo consciente de la diferencia de 1 hora entre la hora que está creando y la zona horaria en la que desea esa hora. Entonces, se supone que representa las 11 am (hora de Denver, la zona solicitada) o las 10 am ( hora de Los Ángeles, la zona de origen)? Creo que podrías defender cualquiera de los dos.

Pedantemente, un objeto Date es diferente tanto de una cadena de fecha ISO como de una cadena DTSTART, ya que representa una marca de tiempo en milisegundos UTC que no tiene concepto de zona horaria o desplazamiento. Así que creo que tiene más sentido representar la marca de tiempo de la hora deseada _en la zona correcta_ lo que significa que si estás en Los Ángeles y quieres representar la hora en Denver, tienes que pasar un objeto Fecha inicializado con el relativo compensación aplicada (por lo que el ejemplo anterior representaría las 11 am en Denver). Sin embargo, esto es confuso, ya que si luego viajas a Nueva York y quieres representar el mismo tiempo, ¡tienes que escribir new Date(2018, 10, 1, 13, 0, 0) !

Una ventaja de tirar todo el concepto de fechas locales por la ventana es que no tienes que lidiar con esto; la fecha y la hora representadas pueden tratarse como una hora flotante, capaz de proyectarse en cualquier zona horaria sin realizar ninguna conversión.

Esto se complica, por supuesto, por el comportamiento de motores como Chrome.

Para sus propósitos, probablemente pueda obtener los valores que desea haciendo:

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

Actualmente estoy tratando de decidir si esto pertenece a la biblioteca propiamente dicha...

¡Gracias! Tu respuesta realmente me aclaró las cosas. Creo que estaba cansado y quemado ayer cuando estaba tratando de entender todo. Ahora veo lo que está sucediendo, y tienes razón, estaba pasando en el momento equivocado al usar el tzid .

Para sus propósitos, probablemente pueda obtener los valores que desea haciendo:

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

Actualmente estoy tratando de decidir si esto pertenece a la biblioteca propiamente dicha...

¡Esto funcionó para mí! Muchas gracias. Sería bueno si tal vez hubiera una manera de configurar lo que devuelven rule.all() y rule.between() para que no introduzcamos otro cambio importante, pero aún podamos obtener el comportamiento anterior de forma nativa. No estoy seguro de cómo lograr eso cuando también considera que me gusta usar rrulestr() o RRule.fromString() pero tal vez un argumento adicional o algo así para designar qué tipo de fecha se devuelve.


ACTUALIZACIÓN: el d.getUTCMilliseconds() anterior debe cambiarse a d.getUTCSeconds()

Entonces, su solución funciona cuando la zona horaria de mi sistema es la misma que la zona horaria solicitada, pero me está costando mucho tratar de averiguar cómo hacer que RRule devuelva las fechas en la zona horaria solicitada cuando difiere de la zona horaria del sistema. Siempre devuelve fechas representadas en la zona horaria del sistema, en lugar de representadas en la zona horaria solicitada. Ejemplo:

  1. Configure la zona horaria de su sistema operativo en America/Denver
  2. En la consola de herramientas de desarrollo de Chrome, ingrese:
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()
));

El resultado es correcto (para lo que espero/necesito):

(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. Cambiar la zona horaria del sistema operativo a America/New_York
  2. Ejecute el mismo comando en la consola de devtools de Chrome.

El resultado difiere porque se representa en la zona horaria del sistema:

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

Pero quiero que el resultado represente la hora America/Denver , no la zona horaria de mi sistema ni el UTC verdadero. ¿Cómo puedo rezonificarlo para que sea America/Denver ? No estoy familiarizado con Luxon, así que probé moment() en su lugar:

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

Pero esto devuelve lo mismo mientras está en el horario de Nueva York, porque toDate() instantáneamente lo vuelve a colocar en la zona horaria del sistema (en Chrome, al menos).

@shorlbeck Si nota, tanto el resultado en MDT como el EDT son los mismos en UTC, entonces mi pregunta es: ¿está seguro de que desea usar el TZID y no solo el tiempo flotante? Recomiendo el tiempo flotante (básicamente lo mismo, pero con DSTART:<datetime> en lugar de DSTART;TZID=<timezone>:<datetime> ) si desea que siempre genere las 13:00 independientemente de la zona horaria del sistema.

13:00 pseudo-UTC, es decir. Si desea solucionar el problema de Chrome, aún tendrá que convertir a una fecha local utilizando el método en mi comentario anterior.

@davidgoli Vaya, tienes razón. No me di cuenta de que en realidad no necesito el parámetro tzid después de entender cómo funcionaba realmente el tiempo flotante/pseudo-UTC. Y eso significa que tampoco necesito la solución .map() anterior. Tu comentario me apuntó en la dirección correcta. Muchas gracias.

Estaría mintiendo si dijera que esto no fue lo más confuso en lo que he trabajado en mucho tiempo, pero me alegro de estar en la dirección correcta ahora.

¿Ha considerado usar una representación explícita de lo que necesita (fechas flotantes sin zona horaria)? Usar la fecha JS nativa con la zona horaria UTC y afirmar que es UTC en los documentos es extremadamente confuso porque funciona hasta que te golpea con errores de horario de verano.

Sugeriría un objeto, llamémoslo "RRuleDate" que toma un constructor (year, month, day, hours?, minutes?, seconds?, milliseconds?) y tiene un método .toDate() .

Este RRuleDate es lo que before() , after() , between() y all() deberían devolver, y lo único que before() , after() , between() y la opción dtstart deberían aceptarse como parámetros.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

kirrg001 picture kirrg001  ·  5Comentarios

fatshotty picture fatshotty  ·  5Comentarios

espen picture espen  ·  11Comentarios

spurreiter picture spurreiter  ·  3Comentarios

Prinzhorn picture Prinzhorn  ·  15Comentarios