Rrule: A alteração do horário de verão não foi tratada corretamente, apesar do "Suporte ao fuso horário"

Criado em 20 nov. 2018  ·  21Comentários  ·  Fonte: jakubroztocil/rrule

Eu tenho comparado o comportamento desta biblioteca (v2.5.6) no front-end com a biblioteca PHP equivalente (v2.3.3) no back-end. Há uma inconsistência definitiva com a maneira como a mudança de horário de verão é tratada, e acredito que a versão PHP lida melhor com isso.

Exemplo de código:

No fuso horário America/Denver , uma mudança de horário de verão ocorre no domingo, 4 de novembro de 2018 (passando de GMT-6 para GMT-7). Então, vamos configurar uma série recorrente a partir das 13h, repetindo todas as segundas, quartas e quintas, começando em 1º de novembro, antes da mudança de horário:

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

Nesse caso, espero que todas as recorrências comecem às 13h, conforme abaixo. (De acordo com os documentos, estou usando a biblioteca Luxon. Não vi nada dizendo que precisava inicializá-la de alguma forma, então estou assumindo que ela está "instalada" corretamente por meio do fio.)

Resultado esperado (todos a partir das 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 atual:

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

Quando configurei uma situação semelhante na biblioteca PHP, o resultado foi o esperado acima, com todas as instâncias começando às 13:00:00 no fuso horário America/Denver, não UTC .

Outros detalhes:

  • Versão 2.5.6
  • Mac OS X 10.13.6
  • Chrome 70
  • Minha hora local atual é MST (América/Denver, GMT-7)

Editar: corrigir erro de digitação

Todos 21 comentários

Você está recebendo algum aviso do console? por exemplo:

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

Luxon está em optionalDependencies , então será importado se estiver em seu node_modules mas caso contrário não.

Quando executo seu código no conjunto de testes rule, recebo:

      [
        [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 é o que eu esperaria, já que o horário que você solicitou é 1900 no fuso horário de Denver. (Como isso é JavaScript e não PHP, não podemos fornecer objetos Date em um fuso horário diferente do fuso horário da máquina local ou UTC, então UTC é o que esta biblioteca usa para todos os momentos.)

O fato de você estar vendo datas locais (MST/MDT) e não datas UTC é um pouco desconcertante para mim. Até onde eu sei, esta biblioteca está atualmente configurada para retornar apenas datas UTC, embora anteriormente costumava retornar datas locais.

Você está recebendo algum aviso do console? por exemplo:

'O uso de TZID sem Luxon disponível não é suportado. Os horários retornados estão em UTC, não no fuso horário solicitado'

Nenhum aviso do console. Parece que Luxon é fornecido corretamente.

No que diz respeito aos fusos horários, recebo MST/DST como resultado da execução do meu código diretamente na linha de comando do console Chrome devtools.

Etapas: Abra o Chrome -> Navegue até a página com RRule e Luxon fornecidos -> Cmd + Opt + I -> guia "Console" -> copie e cole acima do trecho na linha de comando na parte inferior -> Enter

esta biblioteca está atualmente configurada para retornar apenas datas UTC, embora anteriormente costumava retornar datas locais.

Você sabe em qual versão isso mudou? Por que essa mudança seria feita? Retornar apenas as datas UTC na verdade introduz essa discrepância de horário de verão! Um evento que sempre ocorre às 13:00 UTC praticamente garante que o evento começará em um horário diferente antes/depois de uma mudança de horário de verão. Eu acho que essa mudança deve ser revertida, ou então outro método alternativo deve ser fornecido que permita que o horário de verão seja contabilizado. O UTC é extremamente útil, mas nem sempre é a abordagem correta em todas as situações. Ao trabalhar com eventos recorrentes, temos que levar em conta o fuso horário específico em que o evento ocorre especificamente porque o horário de verão é uma coisa.

Na verdade, recebo um comportamento diferente do Chrome do que do Node.

Eu diria que não entendo por que sua saída "esperada" é o que você realmente espera. Esses horários parecem incorretos.

O que _seria_ correto são os horários se você adicionasse o deslocamento TZ para cada horário ao horário local fornecido. Então todos os horários serão 19:00. É por isso que o RRule lida com UTC e por que seu uso de UTC não tem nada a ver com esse bug específico.

De fato, se você fizer:

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

você verá as strings ISO resultantes com consistentemente as datas e horas corretas.

Parece, além disso, que o Chrome não suporta o retorno de datas em 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!

O RRule usa Luxon para gerar a hora local correta para _todos os fusos horários do mundo_, não apenas para o fuso horário local. É por isso que o uso do UTC é crítico: se o código do fuso horário retornasse datas "corretas" para o fuso horário local, mas você precisasse gerar datas em um fuso horário diferente (dado com o parâmetro TZID ), você obteria resultados extremamente incorretos. Estamos usando "datas UTC" não porque estamos fazendo cálculos de horário de verão em UTC (não estamos, você pode inspecionar o conjunto de testes e ver muitos exemplos de cálculos corretos de horário de verão), mas porque os fusos horários na especificação do JavaScript são horrivelmente quebrado e UTC é o mais próximo de um conceito de data "neutro" disponível. Lamento que seja confuso trabalhar e que o uso desta biblioteca não possa ser mais claro (embora esteja aberto a sugestões).

O que _é_ preocupante é o problema de compatibilidade do navegador que você levanta. Eu pediria que você tentasse agora seu próprio código de exemplo em um console Node em execução ou no Firefox para esse assunto. Você verá resultados muito diferentes (e corretos).

Portanto, o verdadeiro problema aqui é que os resultados são confusos no Chrome. Acho que vale a pena abordar isso, embora não esteja imediatamente claro para mim exatamente como fazê-lo. O que eu posso dizer é que ambos os ambientes geram exatamente as mesmas strings ISO e timestamps.

@davidgoli Você está certo, o Firefox mostra um resultado diferente no console ao executar meu código original. No entanto, o resultado é, em última análise, o mesmo tempo, é apenas que o resultado é expresso em hora UTC no Firefox, enquanto no Chrome é expresso em America/Denver hora (ou provavelmente qualquer que seja o fuso horário local).

Eu diria que não entendo por que sua saída "esperada" é o que você realmente espera. Esses horários parecem incorretos.

O que seria correto seriam os horários se você adicionasse o deslocamento TZ para cada horário ao horário local fornecido. Então todos os horários serão 19:00. É por isso que o RRule lida com UTC e por que seu uso de UTC não tem nada a ver com esse bug específico.

Deixe-me tentar explicar por que NÃO queremos 19:00 UTC para cada evento recorrente.

Digamos que um usuário queira agendar um evento repetido em um local específico em algum lugar no fuso horário America/Denver para todas as quartas-feiras às 13h. Simplificando, isso significa - não importa a época do ano - o usuário espera que esse mesmo evento ocorra toda quarta-feira às 13h, sempre que for quarta-feira às 13h naquele local . Nem 12h, nem 14h. Sempre às 13h para o horário da América/Denver.

No entanto, rule está resolvendo as recorrências de acordo com o horário UTC -- mesmo quando um fuso horário é passado. Ao fazer isso, está basicamente dizendo: "Ah, 1:00 pm América/Denver avalia para 19:00. Portanto, 19:00 deve SEMPRE se referir às 13h nos Estados Unidos/Denver, durante todo o ano.Mas isso não é verdade. Antes de 4 de novembro de 2018, isso é verdade, mas depois de 4 de novembro, a América/Denver sai do horário de verão; nesse ponto, as 19:00 UTC agora são avaliadas como 12:00, hora da América/Denver . Portanto, nosso usuário é informado, incorretamente, que o evento está acontecendo às 12h, não às 13h.

Em outras palavras, 1pm America/Denver pode ser 19:00 ou 20:00 UTC dependendo se estamos no horário de verão ou não, e é impossível usar rule para obter as recorrências corretas porque quer dizer que 19: 00 é a hora correta durante todo o ano.

Eu sinto que a regra está acreditando no mito que a maioria dos programadores acredita, que é que o UTC é sempre a solução correta ao lidar com datas e horários. Mas há certos casos em que não é a melhor solução, e este exemplo (repetir eventos futuros em um local específico) é um deles.


Edit: Em teoria, estou bem com rule retornando os horários em UTC, mas esses horários devem estar corretos para o fuso horário determinado, quando um fuso horário é passado. Isso significa que devemos ver 19:00 UTC mudar para 20:00 UTC às algum ponto no conjunto de resultados, para o exemplo que está sendo discutido aqui.

Deixe-me voltar aqui - eu acho que isso é um problema de documentação.

RRule _sempre_ retorna datas JS "UTC". Isso não significa, no entanto, que essas datas devem representar o horário UTC. Em vez disso, devido às limitações da única alternativa - datas locais - foi tomada a decisão de que _somente_ datas JS com deslocamento de fuso horário 0 são utilizáveis ​​para a matemática de datas que esta biblioteca está fazendo. Isso tem o efeito colateral enganoso de parecer que esses horários realmente representam a hora em UTC, quando na verdade eles devem ser interpretados no fuso horário representado pelo parâmetro TZID . Assim, por exemplo, no Firefox, o comportamento pretendido é a biblioteca retornar a você uma Data representando 19:00 e, como você solicitou que esse horário fosse o horário de Denver, ele estará correto para o horário de Denver. Você deve ignorar o fato de que essa data informa que é uma data UTC, porque esse é o único tipo de data útil em JavaScript. Em vez disso, você deve usar o conhecimento de que esta regra tem o fuso horário de Denver para interpretar 19:00 como sendo a hora local em Denver.

Isso fica mais claro quando você está usando fusos horários fora da zona de sua máquina local. Por exemplo, se você configurar sua RRule com o nome da zona America/New_York , verá que as datas são retornadas como 17:00, porque esta é a hora local no fuso horário da sua máquina (Denver) quando é 19 :00 no fuso horário solicitado (Nova York).

O JavaScript não tem como "fixar" uma data em um fuso horário específico, portanto, sempre representamos a data e a hora corretas, não importa o que aconteça, e você pode descartar o fato de que a Data se informa como UTC. Quando os fusos horários são usados ​​no RRule, na verdade é um horário local. É por isso que o Firefox fornece a hora local correta como 19:00 (mesmo que essa data seja informada como UTC, na verdade é a hora de Denver).

Isso esclarece as coisas em tudo? Eu entendo que é confuso e não intuitivo, mas espero que seja simples o suficiente para entender que, uma vez entendido, fica fácil raciocinar. Nós definitivamente deveríamos documentar isso melhor.

Uma coisa que eu gostaria de explorar a partir disso é a possibilidade de usar Luxon para redefinir as datas resultantes no fuso horário local, para que seja pelo menos consistente com suas expectativas. Acho que isso não deve ser muito difícil e deve ajudar a esclarecer mal-entendidos como este. O problema do Chrome é genuinamente novo para mim e é uma má notícia.

No entanto, essa seria uma mudança inovadora, portanto, gostaria de oferecer a oportunidade de feedback da comunidade antes do envio.

@shorlbeck Este diff, acredito, implementa o comportamento que você 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(),
+    ])
+  })
 })

No entanto, não estou totalmente convencido de que este é o caminho a seguir para esta biblioteca. dada a abordagem geral da biblioteca de retornar tempos "flutuantes" que sempre devem ser interpretados no fuso horário local do cliente (embora com deslocamento 0). O getter apropriado para as datas retornadas por rrule são os getters getUTC* , então, por exemplo, se você usar getUTCHours() em cada data em seu resultado original, você obterá o horas corretas, mesmo no Chrome.

Estou inclinado a pensar que a correção correta aqui não é alterar o comportamento desta biblioteca, mas documentar seu uso um tanto idiossincrático de tempos pseudo-"UTC" (na verdade "flutuantes") com mais clareza. Estou aberto à persuasão sobre isso, no entanto.

Atualizei o README com alguns esclarecimentos e instruções para seu caso de uso. No entanto, estou interessado em continuar essa discussão e aberto a possivelmente alterar esse comportamento em uma versão futura da biblioteca.

Obrigado pela resposta e pela explicação detalhada.

Infelizmente, nunca estive tão confuso.

Trabalhar com datas e horas é difícil o suficiente quando esses horários são na verdade UTC ou na verdade um horário local, mas ter que lembrar também que às vezes um horário UTC não é realmente um horário UTC é mais do que meu cérebro pode lidar. Passei o dia todo tentando entender o que você escreveu, mas no final do dia, é tão completamente contra-intuitivo que eu simplesmente não consigo entender.

Sempre que olho para o código e vejo uma data "UTC", não me lembro se é realmente UTC ou apenas pseudo-UTC , especialmente quando você adiciona o problema do Chrome lá. Está tornando quase impossível para mim codificar. Está arruinando minha confiança na legibilidade do meu código.

Aqui está a minha pergunta, então... Voltando ao meu exemplo original... No UTC verdadeiro, 2018-11-01 19:00:00 refere-se às 13:00 no horário da América/Denver (13:00). E no meu exemplo, 1pm America/Denver é a hora correta do meu evento. Mas se o UTC não for UTC verdadeiro, isso significa que devo passar new Date(Date.UTC(2018, 10, 1, 13, 0, 0)) ou new Date(Date.UTC(2018, 10, 1, 19, 0, 0)) ao usar o parâmetro tzid ? (A diferença é 13:00 vs. 19:00)

É pseudo-UTC entrando e saindo? Ou verdadeiro UTC entrando, mas pseudo-UTC saindo? E como posso me lembrar disso com segurança?

Sua regra é especificada como:

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

Mas de acordo com a RRULE RFC5545, isso indica uma regra no fuso horário de Denver com 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

Se você quiser um DTSTART em 1300 hora local de Denver, você deve especificá-lo como 1300 em sua regra. A biblioteca deve estar completamente em conformidade com a especificação a esse respeito. (Bem, exceto pelo pequeno fato de que retornará 1300 UTC ...)

Além disso, certifique-se de ler a seção sobre "tempo flutuante":

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, infelizmente, não oferece uma implementação de "tempo flutuante". A opção mais próxima que temos de um horário "puro" sem fuso horário é o UTC, portanto, "psuedo-UTC". (Para registro, rrule também suporta tempos flutuantes, especificados nem com um TZID nem com um designador Z UTC.)

A mudança para UTC foi totalmente iniciada com este commit: https://github.com/jakubroztocil/rrule/commit/850ed075175eb1acfcbd7b2cddf0606f2b2206f7

Se você verificar o commit anterior e escolher o teste adicionado de #850ed075, verá que ele falha. Na verdade, centenas de testes estavam falhando, a menos que todo o conjunto fosse executado em UTC. Isso porque, para calcular datas futuras que ultrapassam os limites do horário de verão, tivemos que instanciar as datas em que o relógio atual estava funcionando no horário de verão e, em seguida, calcular a matemática que nos colocou no horário padrão, resultando em um deslocamento incorreto de 1 hora. A implementação atual sempre fornece os horários locais corretos. Agora, todo o conjunto de testes passará de fato em qualquer ambiente local, com a ressalva de que para obter a data/hora correta você deve usar os métodos getUTCDate() / getUTCHour() .

Estou investigando mudar isso em uma versão futura, mas a mudança não é trivial sem adotar uma biblioteca de terceiros necessária como Luxon.

Em uma versão futura, adoraria mudar o comportamento para retornar as datas que estão sempre corretas no seu fuso horário local, por exemplo:

  • DTSTART;TZID=America/Denver:20181101T130000 retornaria uma data 1300-0700 (horário padrão) se você estiver em Denver ou 2000Z se estiver em UTC
  • DTSTART=20181101T130000 lhe daria 1300-0700 se você estiver em Denver e 1300Z se estiver em UTC
  • DTSTART=20181101T130000Z lhe daria 0600-0700 se você estiver em Denver e 1300Z se estiver em UTC

É este o comportamento que você está esperando?

Um grande problema é que um determinado ambiente JS local só pode representar datas em um único fuso horário. Assim, nem new Date(...) nem new Date(Date.UTC(...)) são realmente apropriados quando você deseja representar uma data-hora em um fuso horário específico (que pode ser diferente do seu fuso horário atual).

Então, qual deve ser o comportamento com:

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

se a máquina local estiver em America/Los_Angeles ? Você teria que estar pelo menos um pouco ciente da diferença de 1 hora entre o horário que você está criando e o fuso horário em que você deseja esse horário. hora de Los Angeles, a zona de origem)? Eu acho que você poderia fazer um caso para qualquer um.

De forma pedante, um objeto Date é diferente de uma string de data ISO e uma string DTSTART, pois representa um carimbo de data/hora em milissegundos UTC que não tem conceito de fuso horário ou deslocamento. Então eu acho que faz mais sentido representar o timestamp da hora desejada _na zona correta_ o que significa que se você está em Los Angeles e quer representar a hora em Denver, você tem que passar um objeto Date inicializado com o relativo deslocamento aplicado (portanto, o exemplo acima representaria 11h em Denver). Isso é confuso, pois se você viajar para Nova York e quiser representar ao mesmo tempo, precisará escrever new Date(2018, 10, 1, 13, 0, 0) !

Uma vantagem de jogar todo o conceito de datas locais pela janela é que você não precisa lidar com isso; a data e a hora representadas podem ser tratadas como uma hora flutuante, capaz de ser projetada em qualquer fuso horário sem fazer nenhuma conversão.

Isso é complicado, é claro, pelo comportamento de mecanismos como o Chrome.

Para seus propósitos, você provavelmente pode obter os valores desejados fazendo:

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

Atualmente estou tentando decidir se isso pertence à biblioteca propriamente dita...

Obrigado! Sua resposta realmente esclareceu as coisas para mim. Acho que estava cansado e esgotado ontem quando estava tentando entender tudo. Agora vejo o que está acontecendo, e você está certo, eu estava passando na hora errada ao usar o parâmetro tzid .

Para seus propósitos, você provavelmente pode obter os valores desejados fazendo:

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

Atualmente estou tentando decidir se isso pertence à biblioteca propriamente dita...

Isso funcionou para mim! Muito obrigado. Seria bom se talvez houvesse uma maneira de configurar o que é retornado por rule.all() e rule.between() para que não introduzamos outra alteração importante, mas ainda possamos obter o comportamento acima nativamente. Não tenho certeza de como fazer isso quando você também considera que eu gosto de usar rrulestr() ou RRule.fromString() mas talvez um argumento adicional ou algo para designar que tipo de data é retornada?


ATUALIZAÇÃO: O d.getUTCMilliseconds() acima deve ser alterado para d.getUTCSeconds()

Portanto, sua solução funciona quando o fuso horário do meu sistema é o mesmo que o fuso horário solicitado, mas estou tendo um tempo horrível tentando descobrir como fazer com que o RRule retorne as datas no fuso horário solicitado quando ele difere do fuso horário do sistema. Ele sempre retorna datas representadas no fuso horário do sistema, em vez de representadas no fuso horário solicitado. Exemplo:

  1. Defina o fuso horário do seu sistema operacional para America/Denver
  2. No console do Chrome devtools, digite:
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()
));

O resultado está correto (para o que eu espero/preciso):

(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. Altere o fuso horário do sistema operacional para America/New_York
  2. Execute o mesmo comando no console do Chrome devtools.

O resultado difere, pois é representado no fuso horário do 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)]

Mas eu quero que o resultado represente America/Denver tempo, não o fuso horário do meu sistema, nem o UTC verdadeiro. Como posso rezonear para America/Denver ? Não estou familiarizado com Luxon, então tentei moment() :

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

Mas isso retorna a mesma coisa no horário de Nova York, porque toDate() instantaneamente o coloca de volta no fuso horário do sistema (no Chrome, pelo menos).

@shorlbeck Se você notar, tanto o resultado no MDT quanto o EDT são os mesmos em UTC, então minha pergunta é: você tem certeza de que deseja usar o parâmetro TZID e não apenas o tempo flutuante? Eu recomendo o tempo flutuante (basicamente o mesmo, mas com DSTART:<datetime> em vez de DSTART;TZID=<timezone>:<datetime> ) se você quiser que ele sempre gere 13:00 independentemente do fuso horário do sistema.

13:00 pseudo-UTC que é. Se você quiser contornar o problema do Chrome, ainda precisará converter para uma data local usando o método no meu comentário anterior.

@davidgoli Oh uau, você está certo. Eu não percebi que na verdade não preciso do parâmetro tzid depois que entendi como o tempo flutuante/pseudo-UTC realmente funcionava. E isso significa que também não preciso da solução .map() acima. Seu comentário me apontou na direção certa. Muito obrigado.

Eu estaria mentindo se dissesse que esta não foi a coisa mais confusa em que trabalhei em muito tempo, mas estou feliz por estar indo na direção certa agora.

Você já considerou usar uma representação explícita do que você precisa (datas flutuantes sem fuso horário)? Usar o JS Date nativo com fuso horário UTC e alegar que é UTC nos documentos é extremamente confuso porque funciona até que você encontre bugs de horário de verão.

Sugiro um objeto, vamos chamá-lo de "RRuleDate" que recebe um construtor (year, month, day, hours?, minutes?, seconds?, milliseconds?) e tem um método .toDate() .

Este RRuleDate é o que before() , after() , between() e all() devem retornar, e a única coisa que before() , after() , between() e a opção dtstart devem aceitar como parâmetros.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

maconfr picture maconfr  ·  6Comentários

fatshotty picture fatshotty  ·  5Comentários

shavenwalrus picture shavenwalrus  ·  7Comentários

grigio picture grigio  ·  7Comentários

espen picture espen  ·  11Comentários