Rrule: TZIDを使用したDTSTART:betweenは誤った時間を生成します

作成日 2019年07月16日  ·  16コメント  ·  ソース: jakubroztocil/rrule

問題の報告

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

これを出力します:

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

15:15:00 GMT-0500 (CDT)は17:15 PDTですが、DTSTARTはDTSTART;TZID=America/Los_Angeles:20190603T181500

@davidgoliによって実装されたtzidを設定すると、異なる結果が得られます。

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

期待される結果は次のとおりです。

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

これの理由は何ですか?
rruleバージョン:2.6.2
CDTタイムゾーンでノードを実行します。 私のシステムタイムゾーンはAmerica/Los_Angeles

全てのコメント16件

私はこのレポをクローンし、このテストを追加しました:

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

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

UTCタイムゾーンで実行した場合( launch.json "env": { "TZ": "UTC" }を設定した場合)にのみ成功します。 テストは他のタイムゾーンでは失敗します。
@davidgoliはこれが意図されたものであり、私はここで何かが欠けていますか? はいの場合、 tzidパラメータを適切に使用するにはどうすればよいですか? rruleに指定されたタイムゾーンで実行するように指示するために追加されたと思います。

これと同じ問題がありますが、rrule.all()があります。 UTCは機能しますが、他のタイムゾーンでは、オフセットが適用されるべきでないときに再度適用されます。

rrule-tz.jsの1849行目と関係があるのではないかと思います。

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded 、その名前とは異なり、UTCでない限り、チェックを実行せず、常に再ゾーン化します。

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

すでに正しくゾーニングされているにもかかわらず、日付が再ゾーニングされているようです。

rezonedDate()を単にthis.date返すように変更すると、問題が解決するようです。 どこかで何かを壊したかどうかは完全にはわかりませんが、大まかに見ると、この変更を行うと他のすべてが正常に機能しているように見えます。

rrule-tz.jsの1849行目と関係があるのではないかと思います。

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded 、その名前とは異なり、UTCでない限り、チェックを実行せず、常に再ゾーン化します。

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

すでに正しくゾーニングされているにもかかわらず、日付が再ゾーニングされているようです。

これは正しくありません。 rezonedDateメソッドは、ゾーンオフセットを適用するかどうかを決定するためにtzidが設定されているかどうかをチェックします。

@agordeevこの問題を調査します。 @ hlee5zebraのタイムゾーンサポートを削除するだけの「修正」は必要ありませんでした。 どちらの場合もtzidパラメータを使用しないと、期待どおりの結果が得られますか?

とにかく、RRuleとRRuleSetの動作の不一致は私に関係しており、おそらくもっと深く調べる必要があるバグです。 テストケースをありがとう!

問題の原因となった問題が表示されます。サーバーコードがDTSTART値の時刻を正しく処理しておらず、TZIDで指定されたタイムゾーンにローカライズする必要があるときにUTCで渡されていました。 おそらくそれはあなたの問題@agordeevであなたを助けるかもしれません。 私を正しい方向に向けてくれてありがとう@davidgoli

コードベースの問題に遭遇した可能性があります。 toString()中にメソッドdatewithzone.ts TZIDが供給される出力UTC時刻を、それが必要代わりに出力ローカライズされた時間になります。

@ hlee5zebraは、READMEでこのテキストに注意してください。

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

重要:UTC日付を使用してください

JavaScriptの日付には注意が必要です。 RRuleは、必要なサードパーティの大きな依存関係を追加せずに、可能な限り多くの柔軟性をサポートしようとしますが、それは、いくつかの特別なルールもあることを意味します。

デフォルトでは、RRuleは「フローティング」時間またはUTCタイムゾーンを扱います。 特定のタイムゾーンで結果が必要な場合、RRuleはタイムゾーンのサポートも提供します。 いずれにせよ、JavaScriptの組み込みの「タイムゾーン」オフセットは邪魔になる傾向があるため、このライブラリはそれをまったく使用しません。 JavaScriptには存在しなかったかのように、すべての時間がゼロオフセットで返されます。

一番下の行は、返される「UTC」の日付は常にローカルタイムゾーンの日付として解釈されることを意味します。 これは、オフセットが適用された「正しい」現地時間を取得するために、追加の変換を行う必要があることを意味する場合があります。

このため、UTCでタイムスタンプを使用することを強くお勧めします。 new Date(Date.UTC(...))。 返される日付も同様にUTCになります(常にタイムゾーンオフセットのある日付を返すChromeを除く)。

追加のしわはほとんどのJS実装にあり、UTCオフセットまたはローカルオフセットのいずれかを取得しますが、それらを切り替えることはできません。 これも実装によって異なります。 したがって、「UTC日付」が返され、一部の場所ではオフセットが含まれ、他の場所では含まれないtoString()が返される場合があります。

たとえば、Chromeの場合:

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

しかし、ノードでは:

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

これが、最良の結果を得るには、 toString()値を無視し、 toISOString()およびgetUTCHours() (など)メソッドのみを使用する理由です。

このアプローチ(すべての日付にUTCメソッドのみを使用し、それらを現地時間であると「解釈」する)により、rruleのタイムゾーンを考慮する必要なしに、rruleによって返される日付と時刻に統一的にアクセスできることに注意してください。

さて、私はこの問題を再現可能にするために少しの間rrule.all()をいじくり回してきました、そしてこれがこの問題を確実に再現するために私が見つけたものです:

  1. 'America / Adak'、 'America / Chicago'(私の現地時間)、 'America / New_York'、および 'UTC'のそれぞれのタイムゾーン内でDTSTARTを深夜に設定してみました。結果のrrule.toString()は正確に印刷されます:
>> rRule.toString()
"DTSTART;TZID=America/Adak:20190718T000000
RRULE:FREQ=DAILY"

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

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

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

渡されるイテレータ機能を歩いている間rrule.all() 、最初のパラメータの最初のインスタンスがdateターンアウトされる各場合の各時間帯については、次のDate介して印刷されています.toISOString()

アメリカ/アダック:
「2019-07-18T04:00:00.000Z」

アメリカ/シカゴ:
「2019-07-18T00:00:00.000Z」

アメリカ/ニューヨーク:
「2019-07-17T23:00:00.000Z」

UTC:
「2019-07-18T00:00:00.000Z」

タイムゾーンがUTCに設定されていない場合(たとえば、America / Adak、America / New_York)、現地時間と選択したタイムゾーンの間のオフセットがDTSTART日付から差し引かれるように見えます。 つまり、私の現地時間とニューヨークの間のオフセットが+1であるため、ニューヨークのISO文字列は23:00を示しています。これを、2019年7月18日の深夜から差し引くと、07/17の午後11時になります。 / 2019。

ただし、これはUTCでは発生しないことに注意してください。これは興味深いことです。

これが問題である可能性があると思いますか、それとも私が見逃した可能性のある構成を行う必要がありますか?

私が見ているものの回避策として、私は次のことを行いました、そしてこれは私にJavaScriptのDateの正確なインスタンスを確実に得るようです:

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

    ...
}.bind(this));

@ hlee5zebraはい、 getUTCxxxメソッドを使用するアプローチは、Readmeで推奨されている正しいアプローチです。

疑似UTC日付がUTC時間で「実際に」発生することはほとんどないことに注意してください。 UTCは「ニュートラル」タイムゾーンになるようにオーバーロードされているため、元のゾーンに関係なく、 getUTCxxxメソッドを使用して_local_時間を取得できます。 このため、ローカルタイムゾーンでローカルタイムゾーンを使用する場合と同じように、 tzidを使用せずに同じ動作が見られることを期待する必要があります。 tzidは、_別のタイムゾーンでの_再発の現在の現地時間を取得するためにのみ使用する必要があります。 繰り返しを常にユーザーのローカルタイムゾーンに設定する場合は、 tzid使用しないでください。

これが、このライブラリを書き直しても、組み込みのJSDateオブジェクトがまったく使用されない理由です。 混乱しすぎです。

@agordeevこの問題を調査します。 @ hlee5zebraのタイムゾーンサポートを削除するだけの「修正」は必要ありませんでした。 どちらの場合もtzidparamを使用しないと、期待どおりの結果が得られますか?

返信ありがとうございますDavid。

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

生成:

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

したがって、日付/時刻は正しいですが、タイムゾーンはUTCです。 tzid paramを省略すると、rruleはUTCでDTSTARTを考慮します。

繰り返しを常にユーザーのローカルタイムゾーンに設定する場合は、tzidを使用しないでください。

ユーザーのタイムゾーンで繰り返しを取得する唯一のオプションは、そのタイムゾーンをtzid paramに渡すことだと思いましたか? ライブラリはサーバー上のnode.jsで使用されることに注意してください。

_タイムゾーンに関係なく_ユーザーの現地時間で再発が1800になる場合は、 tzidを使用する必要はありません。 READMEで説明されている「フローティング」時間について必ずお読みください: https

このページは役に立ちましたか?
0 / 5 - 0 評価

関連する問題

espen picture espen  ·  10コメント

elazar picture elazar  ·  18コメント

fatshotty picture fatshotty  ·  5コメント

espen picture espen  ·  11コメント

zeluspudding picture zeluspudding  ·  11コメント