Rrule: Perubahan Musim Panas tidak ditangani dengan benar meskipun "Dukungan Zona Waktu"

Dibuat pada 20 Nov 2018  ·  21Komentar  ·  Sumber: jakubroztocil/rrule

Saya telah membandingkan perilaku library ini (v2.5.6) di front-end dengan library PHP yang setara (v2.3.3) di back-end. Ada inkonsistensi yang pasti dengan cara menangani perubahan penghematan siang hari, dan saya yakin versi PHP menanganinya dengan lebih baik.

Contoh Kode:

Di zona waktu America/Denver , pergantian musim panas terjadi pada hari Minggu, 4 November 2018 (mulai dari GMT-6 hingga GMT-7). Jadi mari kita siapkan serial berulang mulai jam 1 siang, berulang setiap Sen, Rab, Kam, mulai 1 Nov yaitu sebelum pergantian waktu:

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

Dalam hal ini saya mengharapkan semua pengulangan dimulai pada pukul 1:00 siang seperti di bawah ini. (Sesuai dengan dokumen, saya menggunakan perpustakaan Luxon. Saya tidak melihat apa pun yang mengatakan bahwa saya harus menginisialisasinya, jadi saya berasumsi itu "diinstal" dengan benar melalui benang.)

Hasil yang diharapkan (semua dimulai pukul 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)
]

Hasil sebenarnya:

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

Ketika saya mengatur situasi serupa di perpustakaan PHP, hasilnya seperti yang diharapkan di atas, dengan semua instance dimulai pada 13:00:00 di zona waktu Amerika/Denver, bukan UTC .

Detail lainnya:

  • Versi 2.5.6
  • Mac OS X 10.13.6
  • Chrome 70
  • Waktu lokal saya saat ini adalah MST (Amerika/Denver, GMT-7)

Sunting: Perbaiki kesalahan ketik

Semua 21 komentar

Apakah Anda mendapatkan peringatan konsol? misalnya:

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

Luxon ada di optionalDependencies , jadi itu akan diimpor jika ada di node_modules Anda tetapi sebaliknya tidak.

Ketika saya menjalankan kode Anda di rrule test suite, saya mendapatkan:

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

itulah yang saya harapkan, karena waktu yang Anda minta adalah 1900 di zona waktu Denver. (Karena ini adalah JavaScript dan bukan PHP, kami tidak dapat memberi Anda objek Date dalam zona waktu selain zona waktu mesin lokal atau UTC Anda, jadi UTC adalah yang digunakan perpustakaan ini sepanjang waktu.)

Fakta bahwa Anda melihat tanggal lokal (MST/MDT) dan bukan tanggal UTC agak membingungkan saya. Sejauh yang saya tahu, perpustakaan ini saat ini dikonfigurasi untuk hanya mengembalikan tanggal UTC, meskipun sebelumnya digunakan untuk mengembalikan tanggal lokal.

Apakah Anda mendapatkan peringatan konsol? misalnya:

'Menggunakan TZID tanpa Luxon tersedia tidak didukung. Waktu yang dikembalikan dalam UTC, bukan zona waktu yang diminta'

Tidak ada peringatan konsol. Tampaknya Luxon disediakan dengan benar.

Sejauh zona waktu, saya mendapatkan MST/DST sebagai hasil dari menjalankan kode saya langsung di baris perintah konsol devtools Chrome.

Langkah-langkah: Buka Chrome --> Arahkan ke halaman dengan RRule dan Luxon yang disediakan --> Cmd+Opt+I --> tab "Konsol" --> salin dan tempel cuplikan di atas ke baris perintah di bawah --> Enter

perpustakaan ini saat ini dikonfigurasi untuk hanya mengembalikan tanggal UTC, meskipun sebelumnya digunakan untuk mengembalikan tanggal lokal.

Apakah Anda tahu di versi mana ini berubah? Mengapa perubahan itu akan dilakukan? Mengembalikan hanya tanggal UTC sebenarnya memperkenalkan perbedaan penghematan siang hari ini! Sebuah acara yang selalu terjadi pada 13:00 UTC cukup menjamin bahwa acara tersebut akan dimulai pada waktu yang berbeda sebelum/sesudah pergantian musim panas. Saya pikir perubahan ini harus dikembalikan, atau metode alternatif lain harus disediakan yang memungkinkan Daylight Savings diperhitungkan. UTC sangat berguna tetapi tidak selalu merupakan pendekatan yang benar dalam semua situasi. Saat bekerja dengan acara berulang, kami HARUS memperhitungkan zona waktu spesifik tempat acara terjadi secara khusus karena penghematan siang hari adalah suatu hal.

Memang, saya mendapatkan perilaku yang berbeda dari Chrome daripada yang saya lakukan dari Node.js.

Saya akan mengatakan saya tidak mengerti mengapa output "yang diharapkan" yang Anda berikan adalah apa yang sebenarnya Anda harapkan. Waktu-waktu itu tampaknya salah.

Yang _akan_ benar adalah waktu jika Anda menambahkan offset TZ untuk setiap waktu ke waktu lokal yang ditentukan. Maka semua waktu akan menjadi pukul 19:00. Inilah sebabnya mengapa RRule berurusan dengan UTC, dan mengapa penggunaan UTC-nya tidak ada hubungannya dengan bug khusus ini.

Memang, jika Anda melakukannya:

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

Anda akan melihat string ISO yang dihasilkan dengan tanggal dan waktu yang benar secara konsisten.

Selain itu, tampaknya Chrome sama sekali tidak mendukung pengembalian Tanggal dalam 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 menggunakan Luxon untuk menghasilkan waktu lokal yang benar untuk _semua zona waktu di dunia_, bukan hanya zona waktu lokal Anda. Inilah sebabnya mengapa penggunaan UTC sangat penting: jika kode zona waktu mengembalikan tanggal "benar" untuk zona waktu lokal Anda, tetapi Anda perlu membuat tanggal di zona waktu yang berbeda (diberikan dengan param TZID ), Anda akan mendapatkan hasil yang sangat salah. Kami menggunakan "tanggal UTC" bukan karena kami melakukan matematika DST di UTC (bukan, Anda dapat memeriksa rangkaian pengujian dan melihat banyak contoh perhitungan DST yang benar), tetapi karena zona waktu dalam spesifikasi JavaScript sangat buruk rusak, dan UTC adalah yang paling dekat dengan konsep tanggal "netral" yang tersedia. Saya minta maaf karena membingungkan untuk dikerjakan dan penggunaan perpustakaan ini tidak bisa lebih jelas (meskipun saya terbuka untuk saran).

Yang _is_ khawatirkan adalah masalah kompatibilitas browser yang Anda kemukakan. Saya akan meminta Anda untuk mencoba kode sampel Anda sendiri di konsol Node yang sedang berjalan, atau di Firefox dalam hal ini. Anda akan melihat hasil yang sangat berbeda (dan benar).

Jadi masalah sebenarnya di sini adalah hasilnya membingungkan di Chrome. Saya pikir itu layak untuk ditangani, meskipun tidak segera jelas bagi saya bagaimana tepatnya melakukannya. Apa yang saya _can_ katakan kepada Anda adalah kedua lingkungan menghasilkan string dan cap waktu ISO yang sama persis.

@davidgoli Anda benar, Firefox memang menunjukkan hasil yang berbeda di konsol saat menjalankan kode asli saya. Namun, hasilnya pada akhirnya adalah waktu yang sama, hanya saja hasilnya dinyatakan dalam waktu UTC di Firefox sedangkan di Chrome dinyatakan dalam waktu America/Denver (atau mungkin apa pun zona waktu lokal Anda).

Saya akan mengatakan saya tidak mengerti mengapa output "yang diharapkan" yang Anda berikan adalah apa yang sebenarnya Anda harapkan. Waktu-waktu itu tampaknya salah.

Yang benar adalah waktu jika Anda menambahkan offset TZ untuk setiap waktu ke waktu lokal yang ditentukan. Maka semua waktu akan menjadi pukul 19:00. Inilah sebabnya mengapa RRule berurusan dengan UTC, dan mengapa penggunaan UTC-nya tidak ada hubungannya dengan bug khusus ini.

Izinkan saya mencoba menjelaskan mengapa kami TIDAK menginginkan waktu 19:00 UTC untuk setiap acara berulang.

Katakanlah pengguna ingin menjadwalkan acara berulang di lokasi tertentu di suatu tempat di zona waktu America/Denver untuk setiap Rabu pukul 1 siang. Sederhananya, itu berarti -- tidak peduli jam berapa sekarang -- pengguna mengharapkan peristiwa yang sama ini terjadi setiap Rabu pukul 1 siang setiap hari Rabu pukul 1 siang di lokasi tersebut . Bukan jam 12 malam, bukan jam 2 siang. Selalu pukul 1 siang untuk waktu Amerika/Denver.

Namun, rrule menyelesaikan pengulangan menurut waktu UTC -- bahkan ketika zona waktu dilewati. Dengan melakukan ini, pada dasarnya mengatakan, "Oh, 1:00pm America/Denver mengevaluasi ke 19:00. Oleh karena itu, 19:00 harus SELALU merujuk pada pukul 1:00 siang di Amerika/Denver, sepanjang tahun. " Tapi itu tidak benar. Sebelum 4 November 2018 itu benar, tetapi setelah 4 November, Amerika/Denver mematikan waktu musim panas, pada saat itu, waktu UTC 19:00 sekarang dievaluasi menjadi 12:00 waktu Amerika/Denver . Jadi pengguna kami diberitahu, secara tidak benar, bahwa acara tersebut terjadi pada jam 12 siang, bukan jam 1 siang.

Dengan kata lain, 1pm America/Denver dapat berupa 19:00 atau 20:00 UTC tergantung pada apakah kita menggunakan waktu musim panas atau tidak, dan tidak mungkin menggunakan rrule untuk mendapatkan pengulangan yang benar karena ingin mengatakan bahwa 19: 00 adalah waktu yang tepat sepanjang tahun.

Saya merasa seperti rrule membeli mitos yang diyakini sebagian besar programmer, yaitu bahwa UTC selalu merupakan solusi yang tepat ketika berhadapan dengan tanggal dan waktu. Tetapi ada beberapa contoh di mana itu bukan solusi terbaik, dan contoh ini (mengulangi acara mendatang di lokasi tertentu) adalah salah satunya.


Sunting: Secara teori, saya setuju dengan aturan mengembalikan waktu dalam UTC, tetapi waktu itu harus benar untuk zona waktu yang diberikan, ketika zona waktu dilewati. Itu berarti kita akan melihat 19:00 UTC beralih ke 20:00 UTC pada beberapa titik dalam kumpulan hasil, untuk contoh yang sedang dibahas di sini.

Biarkan saya kembali ke sini - Saya pikir ini adalah masalah dokumentasi.

RRule _always_ mengembalikan tanggal JS "UTC". Namun, ini tidak berarti bahwa tanggal tersebut dimaksudkan untuk mewakili waktu UTC. Sebaliknya, karena keterbatasan satu-satunya alternatif - tanggal lokal - keputusan telah dibuat bahwa _only_ JS tanggal dengan 0 zona waktu offset dapat digunakan untuk matematika tanggal yang dilakukan perpustakaan ini. Ini memiliki efek samping yang menyesatkan bahwa tampaknya waktu ini benar-benar mewakili waktu dalam UTC, padahal sebenarnya mereka dimaksudkan untuk ditafsirkan dalam zona waktu yang diwakili oleh param TZID . Jadi misalnya, di Firefox, perilaku yang dimaksud adalah perpustakaan mengembalikan Anda Tanggal yang mewakili 19:00, dan, karena Anda meminta waktu itu untuk waktu Denver, itu akan benar untuk waktu Denver. Anda dimaksudkan untuk mengabaikan fakta bahwa tanggal ini melaporkan bahwa ini adalah tanggal UTC, karena itu adalah satu-satunya jenis tanggal yang berguna dalam JavaScript. Sebaliknya, Anda dimaksudkan untuk menggunakan pengetahuan bahwa aturan ini memiliki zona waktu Denver untuk menafsirkan 19:00 sebagai waktu lokal di Denver.

Ini lebih jelas ketika Anda menggunakan zona waktu di luar zona mesin lokal Anda. Misalnya, jika Anda mengatur RRule Anda dengan nama zona America/New_York sebagai gantinya, Anda akan melihat tanggal yang dikembalikan sebagai 17:00, karena ini adalah waktu lokal di zona waktu mesin Anda (Denver) ketika 19 :00 di zona waktu yang diminta (New York).

JavaScript tidak memiliki cara untuk "menyematkan" tanggal ke zona waktu tertentu, jadi kami selalu mewakili tanggal dan waktu yang benar, apa pun yang terjadi, dan Anda dapat membuang fakta bahwa Tanggal melaporkan dirinya sebagai UTC. Ketika zona waktu digunakan di RRule, itu sebenarnya adalah waktu lokal. Inilah sebabnya mengapa Firefox memberi Anda waktu lokal yang benar sebagai 19:00 (meskipun tanggal tersebut dilaporkan sebagai UTC, sebenarnya waktu Denver).

Apakah ini memperjelas semuanya? Saya mengerti bahwa ini membingungkan dan tidak intuitif, tetapi mudah-mudahan cukup sederhana untuk dipahami sehingga setelah dipahami menjadi mudah untuk dipikirkan. Kita pasti harus mendokumentasikan ini dengan lebih baik.

Satu hal yang ingin saya jelajahi dari ini adalah kemungkinan menggunakan Luxon untuk mengubah zona tanggal yang dihasilkan ke zona waktu lokal, jadi setidaknya konsisten dengan harapan Anda. Saya pikir ini seharusnya tidak terlalu sulit, dan seharusnya membantu memperjelas kesalahpahaman seperti ini. Masalah Chrome benar-benar baru bagi saya, dan ini berita buruk.

Namun, ini akan menjadi perubahan besar, jadi saya ingin menawarkan kesempatan untuk umpan balik komunitas sebelum pengiriman.

@shorlbeck Perbedaan ini, saya yakin, mengimplementasikan perilaku yang Anda harapkan untuk dilihat:

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

Namun, saya tidak sepenuhnya yakin ini adalah cara untuk pergi ke perpustakaan ini. mengingat pendekatan umum perpustakaan untuk mengembalikan waktu "mengambang" yang selalu dimaksudkan untuk ditafsirkan dalam zona waktu lokal klien (walaupun dengan 0 offset). Getter yang sesuai untuk tanggal yang dikembalikan oleh rrule adalah getter getUTC* , jadi misalnya, jika Anda menggunakan getUTCHours() pada setiap tanggal dalam hasil asli, Anda akan mendapatkan jam yang benar, bahkan di Chrome.

Saya condong ke arah berpikir bahwa perbaikan yang benar di sini bukanlah untuk mengubah perilaku perpustakaan ini, tetapi untuk mendokumentasikan penggunaan pseudo-"UTC" (sebenarnya "mengambang") yang agak aneh dengan lebih jelas. Saya terbuka untuk persuasi tentang ini, namun.

Saya telah memperbarui README dengan beberapa klarifikasi dan instruksi untuk kasus penggunaan Anda. Namun, saya tertarik untuk melanjutkan diskusi ini, dan terbuka untuk kemungkinan mengubah perilaku ini di versi perpustakaan yang akan datang.

Terima kasih atas jawaban dan penjelasan detailnya.

Sayangnya, saya tidak pernah lebih bingung.

Bekerja dengan tanggal dan waktu cukup sulit ketika waktu tanggal tersebut sebenarnya UTC atau sebenarnya waktu lokal, tetapi harus juga diingat bahwa kadang-kadang waktu UTC sebenarnya bukan waktu UTC lebih dari yang dapat ditangani oleh otak saya. Saya telah menghabiskan sepanjang hari hari ini untuk mencoba memahami apa yang Anda tulis, tetapi pada akhirnya, itu sangat berlawanan dengan intuisi sehingga saya tidak dapat memikirkannya.

Setiap kali saya melihat kode dan saya melihat tanggal "UTC", saya tidak dapat mengingat apakah itu sebenarnya UTC atau hanya pseudo-UTC , terutama ketika Anda menambahkan masalah Chrome di sana. Itu membuat saya hampir tidak mungkin membuat kode. Ini merusak kepercayaan saya pada keterbacaan kode saya.

Ini pertanyaan saya, lalu... Kembali ke contoh asli saya... Dalam UTC yang sebenarnya, 2018-11-01 19:00:00 mengacu pada jam 1 siang di waktu Amerika/Denver (13:00). Dan dalam contoh saya, jam 1 siang Amerika/Denver adalah waktu yang tepat untuk acara saya. Tetapi jika UTC tidak benar UTC, apakah itu berarti saya harus melewatkan new Date(Date.UTC(2018, 10, 1, 13, 0, 0)) atau new Date(Date.UTC(2018, 10, 1, 19, 0, 0)) saat menggunakan param tzid ? (Perbedaannya adalah 13:00 vs 19:00)

Apakah pseudo-UTC masuk dan juga keluar? Atau UTC benar masuk tetapi pseudo-UTC keluar? Dan bagaimana saya bisa mengingat ini dengan andal??

Aturan Anda ditentukan sebagai:

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

Tetapi menurut RRULE RFC5545, ini menunjukkan aturan di zona waktu Denver dengan waktu lokal 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

Jika Anda menginginkan DTSTART pada 1300 waktu Denver lokal, Anda harus menetapkannya sebagai 1300 di rrule Anda. Perpustakaan harus benar-benar sesuai dengan spesifikasi dalam hal itu. (Yah, kecuali fakta kecil bahwa itu akan mengembalikan 1300 UTC ...)

Selain itu, pastikan untuk membaca bagian tentang "waktu mengambang":

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, sayangnya, tidak menawarkan implementasi "waktu mengambang". Opsi terdekat yang kita miliki untuk "murni", waktu tanpa zona waktu adalah UTC, karenanya "psuedo-UTC". (Sebagai catatan, rrule mendukung waktu mengambang juga, yang ditentukan tanpa TZID atau Z UTC penunjuk.)

Peralihan ke UTC sepenuhnya dimulai dengan komit ini: https://github.com/jakubroztocil/rrule/commit/850ed075175eb1acfcbd7b2cddf0606f2b2206f7

Jika Anda memeriksa komit sebelumnya, dan memilih tes tambahan dari #850ed075, Anda akan melihatnya gagal. Faktanya, ratusan tes gagal kecuali seluruh rangkaian dijalankan di UTC. Itu karena, untuk menghitung tanggal mendatang yang melintasi batas DST, kami harus membuat instance tanggal saat jam saat ini berjalan di DST dan kemudian menghitung matematika yang memasukkan kami ke dalam Waktu Standar, menghasilkan offset 1 jam yang salah. Implementasi saat ini selalu memberikan waktu lokal yang benar. Sekarang, seluruh rangkaian pengujian sebenarnya akan lulus di lingkungan lokal mana pun, dengan peringatan bahwa untuk mendapatkan tanggal/jam yang benar Anda harus menggunakan metode getUTCDate() / getUTCHour() .

Saya sedang menyelidiki untuk mengubah ini di versi mendatang, tetapi perubahannya tidak sepele tanpa mengadopsi perpustakaan pihak ketiga yang diperlukan seperti Luxon.

Dalam versi yang akan datang, saya ingin mengubah perilaku untuk mengembalikan tanggal yang selalu benar di zona waktu lokal Anda, jadi misalnya:

  • DTSTART;TZID=America/Denver:20181101T130000 akan mengembalikan Anda tanggal 1300-0700 (waktu standar) jika Anda berada di Denver, atau 2000Z jika Anda berada di UTC
  • DTSTART=20181101T130000 akan memberi Anda 1300-0700 jika Anda berada di Denver, dan 1300Z jika Anda berada di UTC
  • DTSTART=20181101T130000Z akan memberi Anda 0600-0700 jika Anda berada di Denver, dan 1300Z jika Anda berada di UTC

Apakah ini perilaku yang Anda harapkan?

Satu masalah besar adalah lingkungan JS lokal yang diberikan hanya dapat mewakili tanggal dalam satu zona waktu. Jadi, baik new Date(...) maupun new Date(Date.UTC(...)) tidak benar-benar sesuai ketika Anda ingin mewakili tanggal-waktu dalam zona waktu tertentu (yang mungkin berbeda dari zona waktu Anda saat ini).

Jadi seperti apa perilaku yang seharusnya:

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

jika mesin lokal ada di America/Los_Angeles ? Anda setidaknya harus menyadari perbedaan 1 jam antara waktu yang Anda buat dan zona waktu yang Anda inginkan. Jadi, apakah itu seharusnya mewakili jam 11 pagi (waktu Denver, zona yang diminta) atau jam 10 pagi ( Waktu Los Angeles, zona asal)? Saya pikir Anda bisa membuat kasus untuk keduanya.

Secara berlebihan, objek Date berbeda dari string tanggal ISO dan string DTSTART, karena objek tersebut mewakili stempel waktu dalam milidetik UTC yang tidak memiliki konsep zona waktu atau offset. Jadi saya pikir paling masuk akal untuk mewakili cap waktu dari waktu yang diinginkan _di zona yang benar_ yang berarti jika Anda berada di Los Angeles dan Anda ingin mewakili waktu di Denver, Anda harus memasukkan objek Date yang diinisialisasi dengan relatif offset diterapkan (jadi contoh di atas akan mewakili jam 11 pagi di Denver). Ini membingungkan, karena jika Anda kemudian bepergian ke New York dan ingin mewakili waktu yang sama, Anda harus menulis new Date(2018, 10, 1, 13, 0, 0) !

Keuntungan membuang seluruh konsep tanggal lokal ke luar jendela adalah Anda tidak harus berurusan dengan ini; tanggal dan waktu yang diwakili dapat diperlakukan sebagai waktu mengambang, yang dapat diproyeksikan ke zona waktu mana pun tanpa melakukan konversi apa pun.

Ini rumit, tentu saja, dengan perilaku mesin seperti Chrome.

Untuk tujuan Anda, Anda mungkin bisa mendapatkan nilai yang Anda inginkan dengan melakukan:

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

Saat ini saya mencoba memutuskan apakah ini termasuk dalam perpustakaan yang tepat ...

Terima kasih! Tanggapan Anda benar-benar menjelaskan banyak hal bagi saya. Saya pikir saya lelah dan terbakar kemarin ketika saya mencoba untuk memahami segalanya. Sekarang saya melihat apa yang terjadi, dan Anda benar, saya melewatkan waktu yang salah saat menggunakan param tzid .

Untuk tujuan Anda, Anda mungkin bisa mendapatkan nilai yang Anda inginkan dengan melakukan:

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

Saat ini saya mencoba memutuskan apakah ini termasuk dalam perpustakaan yang tepat ...

Ini berhasil untuk saya! Terima kasih banyak. Akan lebih baik jika mungkin ada cara untuk mengonfigurasi apa yang dikembalikan oleh rule.all() dan rule.between() sehingga kami tidak memperkenalkan perubahan yang melanggar lagi, tetapi masih bisa mendapatkan perilaku di atas secara asli. Tidak yakin bagaimana mencapainya ketika Anda juga menganggap bahwa saya suka menggunakan rrulestr() atau RRule.fromString() tetapi mungkin argumen tambahan atau sesuatu untuk menunjukkan jenis tanggal apa yang dikembalikan?


UPDATE: d.getUTCMilliseconds() di atas harus diubah menjadi d.getUTCSeconds()

Jadi, solusi Anda berfungsi ketika zona waktu sistem saya sama dengan zona waktu yang diminta, tetapi saya mengalami kesulitan saat mencoba mencari cara agar RRule mengembalikan tanggal di zona waktu yang diminta ketika berbeda dari zona waktu sistem. Itu selalu mengembalikan tanggal yang diwakili dalam zona waktu sistem, bukan diwakili dalam zona waktu yang diminta. Contoh:

  1. Atur zona waktu sistem OS Anda ke America/Denver
  2. Di konsol devtools Chrome, masukkan:
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()
));

Hasilnya benar (untuk apa yang saya harapkan/butuhkan):

(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. Ubah zona waktu sistem OS menjadi America/New_York
  2. Jalankan perintah yang sama di konsol devtools Chrome.

Hasil berbeda, karena diwakili dalam zona waktu sistem:

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

Tetapi saya ingin hasilnya mewakili waktu America/Denver , bukan zona waktu sistem saya, atau UTC yang sebenarnya. Bagaimana saya bisa mengubahnya menjadi America/Denver ? Saya tidak akrab dengan Luxon jadi saya mencoba moment() sebagai gantinya:

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

Tapi ini mengembalikan hal yang sama saat berada di waktu New York, karena toDate() langsung mengembalikannya ke zona waktu sistem (setidaknya di Chrome).

@shorlbeck Jika Anda perhatikan, baik hasil di MDT dan EDT sama di UTC, jadi pertanyaan saya adalah: apakah Anda yakin ingin menggunakan param TZID dan bukan hanya waktu mengambang? Saya merekomendasikan waktu mengambang (pada dasarnya sama, tetapi dengan DSTART:<datetime> alih-alih DSTART;TZID=<timezone>:<datetime> ) jika Anda ingin selalu menghasilkan 13:00 terlepas dari zona waktu sistem.

13:00 pseudo-UTC itu. Jika Anda ingin mengatasi masalah Chrome, Anda masih harus mengonversi ke Tanggal lokal menggunakan metode di komentar saya sebelumnya.

@davidgoli Oh wow, Anda benar. Saya tidak menyadari bahwa saya sebenarnya tidak membutuhkan param tzid setelah saya mengerti bagaimana waktu mengambang/pseudo-UTC benar-benar bekerja. Dan itu berarti saya juga tidak memerlukan solusi .map() di atas. Komentar Anda mengarahkan saya ke arah yang benar. Terima kasih banyak.

Saya berbohong jika saya mengatakan ini bukan hal yang paling membingungkan yang telah saya kerjakan dalam waktu yang lama, tetapi saya senang untuk menuju ke arah yang benar sekarang.

Sudahkah Anda mempertimbangkan untuk menggunakan representasi eksplisit dari apa yang Anda butuhkan (tanggal mengambang tanpa zona waktu)? Menggunakan Tanggal JS asli dengan zona waktu UTC dan mengklaimnya sebagai UTC dalam dokumen sangat membingungkan karena itu berfungsi sampai Anda terkena bug DST.

Saya akan menyarankan sebuah objek, sebut saja "RRuleDate" yang menggunakan konstruktor (year, month, day, hours?, minutes?, seconds?, milliseconds?) dan memiliki metode .toDate() .

RRuleDate ini adalah apa yang before() , after() , between() , dan all() harus dikembalikan, dan satu-satunya hal yang before() , after() , between() dan opsi dtstart harus diterima sebagai parameter.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

grigio picture grigio  ·  7Komentar

fatshotty picture fatshotty  ·  5Komentar

kirrg001 picture kirrg001  ·  5Komentar

zeluspudding picture zeluspudding  ·  11Komentar

elazar picture elazar  ·  18Komentar