Rrule: DTSTART dengan TZID: antara menghasilkan waktu yang salah

Dibuat pada 16 Jul 2019  ·  16Komentar  ·  Sumber: jakubroztocil/rrule

Melaporkan masalah

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)

output ini:

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) adalah 17:15 PDT, sedangkan DTSTART adalah DTSTART;TZID=America/Los_Angeles:20190603T181500

Ketika saya menetapkan tzid diimplementasikan oleh @davidgoli saya mendapatkan hasil yang berbeda:

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) {}

Hasil yang diharapkan adalah:

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) {}

Apa alasannya?
versi aturan: 2.6.2
Saya menjalankan node di bawah zona waktu CDT. Zona waktu sistem saya adalah America/Los_Angeles

Semua 16 komentar

Saya mengkloning repo ini dan menambahkan tes ini:

  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')
    ])
  })

Ini hanya berhasil ketika saya menjalankannya di bawah zona waktu UTC (ketika saya menetapkan "env": { "TZ": "UTC" } di launch.json ). Tes gagal di zona waktu lainnya.
@davidgoli apakah ini dimaksudkan dan saya melewatkan sesuatu di sini? Jika ya, bagaimana cara menggunakan parameter tzid ? Saya pikir itu ditambahkan untuk memberi tahu rrule untuk berjalan di zona waktu yang ditentukan.

Saya memiliki masalah yang sama tetapi dengan rrule.all(). UTC berfungsi, sementara zona waktu lain menerapkan offset lagi saat seharusnya tidak.

Saya pikir itu mungkin ada hubungannya dengan baris 1849 dari rrule-tz.js.

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded , bertentangan dengan namanya, tidak melakukan pemeriksaan apa pun dan selalu mengubah zona kecuali UTC.

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

Sepertinya itu mengubah zona tanggal meskipun sudah dikategorikan dengan benar.

Mengubah rezonedDate() untuk sekadar mengembalikan this.date tampaknya memperbaiki banyak hal untuk saya. Saya tidak dapat sepenuhnya yakin bahwa saya telah merusak sesuatu di tempat lain, tetapi dari pandangan sepintas, sepertinya semuanya berfungsi dengan baik ketika perubahan ini dilakukan.

Saya pikir itu mungkin ada hubungannya dengan baris 1849 dari rrule-tz.js.

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded , bertentangan dengan namanya, tidak melakukan pemeriksaan apa pun dan selalu mengubah zona kecuali UTC.

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

Sepertinya itu mengubah zona tanggal meskipun sudah dikategorikan dengan benar.

Ini tidak benar. Metode rezonedDate memeriksa apakah tzid diatur untuk menentukan apakah akan menerapkan offset zona.

@agordeev Saya akan melihat masalah ini. "Perbaikan" @ hlee5zebra hanya dengan menghapus dukungan zona waktu tidak diperlukan. Apakah Anda mendapatkan hasil yang diharapkan jika Anda tidak menggunakan param tzid dalam kedua kasus tersebut?

Terlepas dari itu, ketidakkonsistenan antara perilaku RRule dan RRuleSet mengkhawatirkan saya dan mungkin bug yang perlu dilihat lebih dalam. Terima kasih untuk kasus ujinya!

Saya melihat masalah yang menyebabkan masalah saya -- kode server saya tidak menangani waktu nilai DTSTART dengan benar, meneruskan UTC ketika harus dilokalkan ke zona waktu yang ditentukan oleh TZID. Mungkin itu bisa membantu Anda dengan masalah Anda @agordeev . Terima kasih @davidgoli telah mengarahkan saya ke arah yang benar.

Sepertinya saya mungkin menemukan masalah di basis kode. Metode toString() dalam datewithzone.ts akan menampilkan waktu UTC saat TZID diberikan, padahal seharusnya menampilkan waktu lokal.

@hlee5zebra pastikan untuk mencatat teks ini di README:

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

Penting: Gunakan tanggal UTC

Tanggal dalam JavaScript rumit. RRule mencoba untuk mendukung fleksibilitas sebanyak mungkin tanpa menambahkan dependensi pihak ketiga besar yang diperlukan, tetapi itu berarti kami juga memiliki beberapa aturan khusus.

Secara default, RRule menangani waktu "mengambang" atau zona waktu UTC. Jika Anda ingin hasil dalam zona waktu tertentu, RRule juga menyediakan dukungan zona waktu. Either way, offset "zona waktu" bawaan JavaScript cenderung hanya menghalangi, jadi perpustakaan ini tidak menggunakannya sama sekali. Semua waktu dikembalikan dengan offset nol, seolah-olah tidak ada di JavaScript.

Intinya adalah tanggal "UTC" yang dikembalikan selalu dimaksudkan untuk ditafsirkan sebagai tanggal di zona waktu lokal Anda. Ini mungkin berarti Anda harus melakukan konversi tambahan untuk mendapatkan waktu lokal yang "benar" dengan offset yang diterapkan.

Untuk alasan ini, sangat disarankan untuk menggunakan stempel waktu di UTC, mis. Tanggal baru(Tanggal.UTC(...)). Tanggal yang dikembalikan juga akan berada di UTC (kecuali di Chrome, yang selalu mengembalikan tanggal dengan offset zona waktu).

Kerutan tambahan ada di sebagian besar implementasi JS, Anda mendapatkan offset UTC _atau_ offset lokal, tetapi Anda tidak dapat beralih di antara keduanya. Ini juga bervariasi tergantung pada implementasinya. Jadi "tanggal UTC" dapat dikembalikan dengan memberikan toString() yang menyertakan offset di beberapa tempat, dan tidak di tempat lain.

Misalnya, di Chrome:

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

tetapi di simpul:

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

Inilah sebabnya mengapa untuk hasil terbaik, abaikan nilai toString() dan secara eksklusif gunakan metode toISOString() dan getUTCHours() (dll).

Perhatikan bahwa pendekatan ini - hanya menggunakan metode UTC untuk semua tanggal, dan kemudian "menafsirkannya" sebagai waktu lokal - memungkinkan cara yang seragam untuk mengakses tanggal & waktu yang dikembalikan oleh rrule _tanpa perlu mempertimbangkan zona waktu rrule_.

Oke, saya telah mengutak-atik rrule.all() sebentar untuk mencoba membuat masalah ini dapat direproduksi, dan inilah yang saya temukan untuk mereproduksi masalah ini dengan andal:

  1. Saya telah mencoba menyetel DTSTART sebagai tengah malam dalam zona waktu masing-masing 'America/Adak', 'America/Chicago' (waktu lokal saya), 'America/New_York', dan 'UTC', dan hasilnya rrule.toString() dicetak secara akurat:
>> 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"

Saat berjalan melalui fungsi iterator yang diteruskan ke rrule.all() , contoh pertama dari parameter pertama date ternyata menjadi yang berikut untuk setiap zona waktu ketika setiap Date dicetak melalui .toISOString() :

Amerika/Adak:
"2019-07-18T04:00:00.000Z"

Amerika/Chicago:
"2019-07-18T00:00:00.000Z"

Amerika/New_York:
"2019-07-17T23:00:00.000Z"

UTC:
"2019-07-18T00:00:00.000Z"

Sepertinya jika zona waktu tidak disetel ke UTC (misalnya Amerika/Adak, Amerika/New_York), maka offset antara waktu lokal Anda dan zona waktu yang dipilih akan dikurangi dari tanggal DTSTART. Jadi string ISO New York menunjukkan 23:00 karena offset antara waktu lokal saya dan New York adalah +1, yang bila dikurangi dari tengah malam pada 18/07/2019, menghasilkan apa yang kita lihat, yaitu pukul 11 ​​malam pada 17/07 /2019.

Perhatikan bahwa ini tidak terjadi untuk UTC, yang aneh.

Apakah menurut Anda ini masalahnya, atau adakah konfigurasi yang perlu dilakukan yang mungkin saya lewatkan?

Sebagai solusi untuk apa yang telah saya lihat, saya telah melakukan hal berikut, dan ini tampaknya memberi saya contoh yang akurat dari 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 Ya, pendekatan Anda menggunakan metode getUTCxxx adalah yang benar yang direkomendasikan oleh readme.

Ingatlah bahwa tanggal pseudo-UTC hampir tidak pernah _benar-benar_ dalam waktu UTC. UTC hanya kelebihan beban untuk menjadi zona waktu "netral", sehingga metode getUTCxxx dapat digunakan untuk mengambil waktu _local_ terlepas dari zona aslinya. Untuk alasan ini, Anda akan melihat perilaku yang sama tanpa menggunakan tzid seperti yang Anda lakukan dengan menggunakan zona waktu lokal Anda, di zona waktu lokal Anda. tzid hanya boleh digunakan untuk mendapatkan waktu lokal saat ini dari pengulangan _di zona waktu yang berbeda_. Jika Anda selalu ingin pengulangan berada di zona waktu lokal pengguna, Anda tidak boleh menggunakan tzid .

Inilah sebabnya mengapa saya menulis ulang perpustakaan ini tidak akan menggunakan objek JS Date bawaan sama sekali. Ini terlalu membingungkan.

@agordeev Saya akan melihat masalah ini. "Perbaikan" @ hlee5zebra hanya dengan menghapus dukungan zona waktu tidak diperlukan. Apakah Anda mendapatkan hasil yang diharapkan jika Anda tidak menggunakan param tzid dalam kedua kasus tersebut?

Terima kasih atas balasan Anda 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]
    })

menghasilkan:

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

Jadi tanggal/waktunya benar, tetapi zona waktunya adalah UTC. rrule mempertimbangkan DTSTART di UTC ketika saya menghilangkan param tzid.

Jika Anda selalu ingin pengulangan berada di zona waktu lokal pengguna, Anda sebaiknya tidak menggunakan tzid.

Saya pikir satu-satunya pilihan untuk mendapatkan pengulangan di zona waktu pengguna adalah meneruskan zona waktu itu ke param tzid? Mengingat perpustakaan digunakan dengan node.js di server.

Jika pengulangan akan berada di 1800 dalam waktu lokal pengguna _terlepas dari zona waktu_, maka Anda tidak perlu menggunakan tzid . Pastikan Anda telah membaca tentang waktu "mengambang" seperti yang dijelaskan dalam README: https://github.com/jakubroztocil/rrule#important -use-utc-dates

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

zeluspudding picture zeluspudding  ·  11Komentar

anthwinter picture anthwinter  ·  11Komentar

elazar picture elazar  ·  18Komentar

espen picture espen  ·  10Komentar

maconfr picture maconfr  ·  6Komentar