Rrule: لم تتم معالجة تغيير التوقيت الصيفي بشكل صحيح بالرغم من "دعم المنطقة الزمنية"

تم إنشاؤها على ٢٠ نوفمبر ٢٠١٨  ·  21تعليقات  ·  مصدر: jakubroztocil/rrule

لقد كنت أقارن سلوك هذه المكتبة (v2.5.6) على الواجهة الأمامية بمكتبة PHP المكافئة (v2.3.3) في النهاية الخلفية. هناك تناقض واضح مع الطريقة التي يتم بها التعامل مع تغيير التوقيت الصيفي ، وأعتقد أن إصدار PHP يتعامل معه بشكل أفضل.

مثال رمز:

في المنطقة الزمنية America/Denver ، يحدث تبديل التوقيت الصيفي يوم الأحد 4 نوفمبر 2018 (الانتقال من GMT-6 إلى GMT-7). لذا ، فلنقم بإعداد سلسلة متكررة تبدأ في الساعة 1 مساءً ، وتكرر كل يوم اثنين ، وأربعاء ، وخميس ، بدءًا من 1 تشرين الثاني (نوفمبر) ، أي قبل تبديل الوقت:

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

في هذه الحالة ، أتوقع أن تبدأ جميع التكرارات في الساعة 1:00 مساءً على النحو التالي. (وفقًا للمستندات ، أنا أستخدم مكتبة Luxon. لم أر أي شيء يقول إن عليّ تهيئتها بطريقة ما ، لذلك أفترض أنها "مثبتة" بشكل صحيح من خلال الغزل.)

النتيجة المتوقعة (جميعها تبدأ في الساعة 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)
]

نتيجة فعلية:

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

عندما أعددت موقفًا مشابهًا في مكتبة PHP ، كانت النتيجة كما هو متوقع أعلاه ، حيث تبدأ جميع المثيلات في الساعة 13:00:00 بتوقيت أمريكا / دنفر ، وليس التوقيت العالمي المنسق (UTC) .

تفاصيل أخرى:

  • الإصدار 2.5.6
  • نظام التشغيل Mac OS X 10.13.6
  • كروم 70
  • التوقيت المحلي الحالي هو MST (America / Denver ، GMT-7).

تحرير: إصلاح الخطأ المطبعي

ال 21 كومينتر

هل تتلقى أي تحذيرات بشأن وحدة التحكم؟ على سبيل المثال:

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

يوجد Luxon في optionalDependencies ، لذلك سيتم استيراده إذا كان في node_modules الخاص بك ولكن ليس بخلاف ذلك.

عندما أقوم بتشغيل الكود الخاص بك في مجموعة اختبار القواعد ، أحصل على:

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

وهو ما كنت أتوقعه ، لأن الوقت الذي طلبته هو 1900 بتوقيت دنفر. (نظرًا لأن هذا هو JavaScript وليس PHP ، لا يمكننا منحك كائنات Date في منطقة زمنية بخلاف المنطقة الزمنية للجهاز المحلي أو التوقيت العالمي المنسق ، لذلك UTC هو ما تستخدمه هذه المكتبة في جميع الأوقات.)

إن حقيقة أنك ترى تواريخ محلية (MST / MDT) وليس تواريخ UTC أمر محير بعض الشيء بالنسبة لي. على حد علمي ، تم تكوين هذه المكتبة حاليًا لإرجاع تواريخ UTC فقط ، على الرغم من استخدامها سابقًا لإرجاع التواريخ المحلية بدلاً من ذلك.

هل تتلقى أي تحذيرات بشأن وحدة التحكم؟ على سبيل المثال:

استخدام TZID بدون توفر Luxon غير مدعوم. الأوقات التي تم إرجاعها بالتوقيت العالمي المنسق ، وليس المنطقة الزمنية المطلوبة

لا تحذيرات وحدة التحكم. يبدو أنه تم توفير Luxon بشكل صحيح.

فيما يتعلق بالمناطق الزمنية ، أحصل على MST / DST نتيجة لتشغيل الكود الخاص بي مباشرة في سطر أوامر وحدة تحكم Chrome devtools.

الخطوات: افتح Chrome -> انتقل إلى الصفحة مع توفير RRule و Luxon -> Cmd + Opt + I -> علامة التبويب "وحدة التحكم" -> نسخ المقتطف أعلاه ولصقه في سطر الأوامر في الأسفل -> إدخال

تم تكوين هذه المكتبة حاليًا لإرجاع التواريخ بالتوقيت العالمي المنسق فقط ، على الرغم من استخدامها سابقًا لإرجاع التواريخ المحلية بدلاً من ذلك.

هل تعرف أي إصدار تغير هذا؟ لماذا يتم إجراء هذا التغيير؟ يؤدي إرجاع تواريخ التوقيت العالمي المنسق فقط في الواقع إلى ظهور هذا التناقض في التوقيت الصيفي! إن الحدث الذي يحدث دائمًا في الساعة 13:00 بالتوقيت العالمي المنسق يضمن إلى حد كبير أن الحدث سيبدأ في وقت مختلف قبل / بعد تبديل التوقيت الصيفي. أعتقد أنه يجب التراجع عن هذا التغيير ، وإلا يجب توفير طريقة بديلة أخرى تسمح بحساب التوقيت الصيفي. التوقيت العالمي المنسق (UTC) مفيد للغاية ولكنه ليس دائمًا النهج الصحيح في جميع المواقف. عند العمل مع الأحداث المتكررة ، يجب أن نأخذ في الاعتبار المنطقة الزمنية المحددة التي يحدث فيها الحدث على وجه التحديد لأن التوقيت الصيفي أمر مهم.

في الواقع ، أحصل على سلوك مختلف من Chrome عما أفعله من Node.

أود أن أقول إنني لا أفهم لماذا ناتجك "المتوقع" هو ما تتوقعه حقًا. تلك الأوقات تبدو غير صحيحة.

ما _w would be_ صحيحًا هو الأوقات إذا أضفت إزاحة TZ لكل مرة إلى التوقيت المحلي المحدد. ثم ستكون جميع الأوقات 19:00. هذا هو السبب في أن RRule تتعامل مع UTC ، ولماذا لا علاقة لاستخدامها لـ UTC بهذا الخطأ المعين.

في الواقع ، إذا قمت بما يلي:

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

سترى سلاسل ISO الناتجة مع التواريخ والأوقات الصحيحة باستمرار.

علاوة على ذلك ، يبدو أن Chrome لا يدعم إرجاع التواريخ بالتوقيت العالمي المنسق على الإطلاق:

> 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 Luxon لإنشاء التوقيت المحلي الصحيح لجميع المناطق الزمنية في العالم ، وليس منطقتك الزمنية المحلية فقط. هذا هو السبب في أن استخدامه لـ UTC أمر بالغ الأهمية: إذا عرض رمز المنطقة الزمنية التواريخ "الصحيحة" لمنطقتك الزمنية المحلية ، ولكنك تحتاج إلى إنشاء تواريخ في منطقة زمنية مختلفة (معطى مع TZID param) ، فستحصل على نتائج غير صحيحة إلى حد كبير. نحن نستخدم "تواريخ التوقيت العالمي المنسق" ليس لأننا نقوم بحسابات التوقيت الصيفي بالتوقيت العالمي المنسق (لسنا كذلك ، يمكنك فحص مجموعة الاختبار والاطلاع على الكثير من الأمثلة لحسابات التوقيت الصيفي الصحيحة) ، ولكن لأن المناطق الزمنية في مواصفات جافا سكريبت مرعبة كسر ، و UTC هو الأقرب إلى مفهوم التاريخ "المحايد" المتاح. أنا آسف لأنه من المحير العمل معه وأن استخدام هذه المكتبة له لا يمكن أن يكون أكثر وضوحًا (على الرغم من أنني منفتح على الاقتراحات).

ما يهم _ هو مشكلة توافق المتصفح التي تطرحها. أود أن أطلب منك الآن تجربة نموذج التعليمات البرمجية الخاص بك في وحدة تحكم Node قيد التشغيل ، أو في Firefox لهذه المسألة. سترى نتائج مختلفة جدًا (وصحيحة).

لذا فإن المشكلة الحقيقية هنا هي أن النتائج محيرة في Chrome. أعتقد أن هذا يستحق المعالجة ، رغم أنه ليس من الواضح على الفور لي بالضبط كيفية القيام بذلك. ما يمكن أن أخبرك به هو أن البيئتين تولدان نفس سلاسل ISO والطوابع الزمنية بالضبط.

davidgoli أنت محق ، يعرض Firefox نتيجة مختلفة في وحدة التحكم عند تشغيل الكود الأصلي الخاص بي. ومع ذلك ، فإن النتيجة في النهاية هي نفس الوقت ، إنها فقط يتم التعبير عن النتيجة بتوقيت UTC في Firefox بينما في Chrome يتم التعبير عنها في America/Denver time (أو ربما أيًا كانت منطقتك الزمنية المحلية).

أود أن أقول إنني لا أفهم لماذا ناتجك "المتوقع" هو ما تتوقعه حقًا. تلك الأوقات تبدو غير صحيحة.

ما سيكون صحيحًا هو الأوقات إذا أضفت إزاحة TZ لكل مرة إلى التوقيت المحلي المحدد. ثم ستكون جميع الأوقات 19:00. هذا هو السبب في أن RRule تتعامل مع UTC ، ولماذا لا علاقة لاستخدامها لـ UTC بهذا الخطأ المعين.

اسمحوا لي أن أحاول شرح سبب عدم رغبتنا في الساعة 19:00 بالتوقيت العالمي المنسق لكل حدث متكرر.

لنفترض أن المستخدم يريد جدولة حدث متكرر في موقع معين في مكان ما في المنطقة الزمنية America/Denver لكل يوم أربعاء في الواحدة مساءً. ببساطة ، هذا يعني - بغض النظر عن الوقت من العام - يتوقع المستخدم أن يكون هذا الحدث نفسه يحدث كل يوم أربعاء في الساعة 1 مساءً كلما كان يوم الأربعاء في الساعة 1 مساءً في ذلك الموقع . ليس 12 م ، وليس 2 م. دائمًا الساعة 1 مساءً بتوقيت أمريكا / دنفر.

ومع ذلك ، تعمل القاعدة على حل التكرارات وفقًا لتوقيت UTC - حتى عند مرور منطقة زمنية. من خلال القيام بذلك ، فإنها تقول في الأساس ، "أوه ، 1:00 مساءً يتم تقييم America / Denver حتى 19:00. لذلك ، 19:00 يجب أن تشير دائمًا إلى الساعة 1:00 ظهرًا في أمريكا / دنفر ، على مدار السنة. " ولكن هذا ليس صحيحًا. قبل 4 نوفمبر 2018 ، كان هذا صحيحًا ، ولكن بعد 4 نوفمبر ، تتوقف أمريكا / دنفر عن التوقيت الصيفي ، وعند هذه النقطة ، يتم تقييم الساعة 19:00 بالتوقيت العالمي المنسق الآن إلى الساعة 12:00 مساءً بتوقيت أمريكا / دنفر . لذلك تم إخبار مستخدمنا ، بشكل غير صحيح ، أن الحدث يحدث في الساعة 12 ظهرًا ، وليس الساعة 1 مساءً.

بمعنى آخر ، يمكن أن تكون الساعة 1 مساءً في أمريكا / دنفر إما 19:00 أو 20:00 بالتوقيت العالمي اعتمادًا على ما إذا كنا في التوقيت الصيفي أم لا ، ومن المستحيل استخدام القاعدة للحصول على التكرارات الصحيحة لأنها تريد أن تقول أن 19: 00 هو الوقت الصحيح على مدار السنة.

أشعر أن القاعدة تشتري الأسطورة التي يعتقدها معظم المبرمجين ، وهي أن UTC هو الحل الصحيح دائمًا عند التعامل مع التواريخ والأوقات. ولكن هناك حالات معينة لا يكون فيها هذا هو الحل الأفضل ، وهذه الحالة (تكرار الأحداث المستقبلية في مكان معين) هي واحدة منها.


تحرير: من الناحية النظرية ، أنا موافق على القاعدة التي تعيد الأوقات بالتوقيت العالمي المنسق ، ولكن هذه الأوقات يجب أن تكون صحيحة بالنسبة للمنطقة الزمنية المحددة ، عندما يتم تجاوز المنطقة الزمنية. وهذا يعني أننا يجب أن نرى التبديل 19:00 بالتوقيت العالمي المنسق إلى الساعة 20:00 بالتوقيت العالمي المنسق في نقطة ما في مجموعة النتائج ، على سبيل المثال الذي تتم مناقشته هنا.

Lemme احتياطيًا هنا - أعتقد أن هذه مشكلة توثيق.

إرجاع RRule _always_ JS التواريخ "UTC". هذا لا يعني ، مع ذلك ، أن تلك التواريخ تهدف إلى تمثيل التوقيت العالمي المنسق (UTC). بدلاً من ذلك ، نظرًا للقيود المفروضة على البديل الوحيد - التواريخ المحلية - تم اتخاذ قرار بأن تواريخ JS _ فقط مع 0 إزاحة المنطقة الزمنية قابلة للاستخدام للتاريخ الرياضي الذي تقوم به هذه المكتبة. هذا له تأثير جانبي مضلل يبدو أن هذه الأوقات تمثل في الواقع الوقت بالتوقيت العالمي المنسق ، في حين أنه من المفترض في الواقع أن يتم تفسيرها في المنطقة الزمنية التي تمثلها المعلمة TZID . على سبيل المثال ، في Firefox ، السلوك المقصود هو أن المكتبة تعيد لك تاريخًا يمثل الساعة 19:00 ، ولأنك طلبت أن يكون ذلك الوقت هو توقيت دنفر ، فسيكون هذا الوقت مناسبًا لتوقيت دنفر. من المفترض أن تتجاهل حقيقة أن هذا التاريخ يشير إلى أنه تاريخ UTC ، لأن هذا هو النوع الوحيد من التاريخ المفيد في JavaScript. بدلاً من ذلك ، من المفترض أن تستخدم معرفة أن هذه القاعدة لها المنطقة الزمنية لدنفر لتفسير الساعة 19:00 على أنها التوقيت المحلي في دنفر.

يكون هذا أكثر وضوحًا عند استخدام مناطق زمنية خارج منطقة جهازك المحلي. على سبيل المثال ، إذا قمت بإعداد RRule الخاص بك باسم المنطقة America/New_York بدلاً من ذلك ، فسترى التواريخ يتم إرجاعها في الساعة 17:00 ، لأن هذا هو التوقيت المحلي في المنطقة الزمنية لجهازك (دنفر) عندما يكون 19 : 00 في المنطقة الزمنية المطلوبة (نيويورك).

ليس لدى JavaScript أي طريقة "لتثبيت" تاريخ بمنطقة زمنية محددة ، لذلك نحن دومًا نمثل التاريخ والوقت الصحيحين بغض النظر عن أي شيء ، ويمكنك تجاهل حقيقة أن التاريخ يعلن نفسه على أنه التوقيت العالمي المنسق (UTC). عندما يتم استخدام المناطق الزمنية في RRule ، فهي في الواقع توقيت محلي. هذا هو السبب في أن Firefox يمنحك التوقيت المحلي الصحيح الساعة 19:00 (على الرغم من الإبلاغ عن هذا التاريخ باسم UTC ، إلا أنه في الواقع وقت دنفر).

هل هذا يوضح الأشياء على الإطلاق؟ أتفهم أنه محير وغير بديهي ، ولكن آمل أن يكون من السهل فهم أنه بمجرد فهمه يصبح من السهل التفكير فيه. يجب علينا بالتأكيد توثيق هذا بشكل أفضل.

شيء واحد أود استكشافه من هذا هو إمكانية استخدام Luxon لإعادة تقسيم التواريخ الناتجة إلى المنطقة الزمنية المحلية ، لذلك فهي على الأقل متوافقة مع توقعاتك. أعتقد أن هذا لا ينبغي أن يكون صعبًا للغاية ، ويجب أن يساعد في توضيح سوء الفهم مثل هذا. مشكلة Chrome جديدة حقًا بالنسبة لي ، وهي أخبار سيئة.

سيكون هذا تغييرًا جذريًا ، ومع ذلك ، فأنا أرغب في تقديم فرصة لتعليقات المجتمع قبل الشحن.

shorlbeck هذا الاختلاف ، على ما أعتقد ، ينفذ السلوك الذي تتوقع رؤيته:

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

ومع ذلك ، لست مقتنعًا تمامًا أن هذه هي الطريقة المثلى لهذه المكتبة. نظرًا للنهج العام للمكتبة في إرجاع الأوقات "العائمة" التي يُقصد دائمًا تفسيرها وفقًا للمنطقة الزمنية المحلية للعميل (وإن كان ذلك بدون إزاحة). المُحضر المناسب للتواريخ التي تم إرجاعها بواسطة rrule هي حروف getUTC* ، لذلك على سبيل المثال ، إذا كنت تستخدم getUTCHours() في كل تاريخ في النتيجة الأصلية ، فستحصل على الساعات الصحيحة ، حتى في Chrome.

أنا أميل إلى التفكير في أن الإصلاح الصحيح هنا ليس تغيير سلوك هذه المكتبة ، ولكن لتوثيق استخدامها الخاص إلى حد ما لمرات "UTC" الزائفة (في الواقع "عائمة") بشكل أكثر وضوحًا. أنا منفتح على الإقناع بشأن هذا ، مع ذلك.

لقد قمت بتحديث README ببعض التوضيحات والتعليمات لحالة الاستخدام الخاصة بك. أنا مهتم بمواصلة هذا النقاش ، ومع ذلك ، وأنا منفتح على احتمال تغيير هذا السلوك في إصدار مستقبلي من المكتبة.

شكرا لك على الرد والشرح التفصيلي.

لسوء الحظ ، لم أكن أكثر حيرة من أي وقت مضى.

يعد العمل مع التواريخ والأوقات صعبًا بما يكفي عندما تكون أوقات التاريخ هذه إما في الواقع UTC أو في الواقع وقتًا محليًا ، ولكن يجب أن نتذكر أيضًا أنه في بعض الأحيان لا يكون وقت UTC في الواقع وقت UTC أكثر مما يستطيع عقلي التعامل معه. لقد أمضيت اليوم كله اليوم أحاول فهم ما كتبته ، لكن في نهاية اليوم ، كان الأمر مخالفًا تمامًا للحدس لدرجة أنني لا أستطيع أن ألتف حوله.

عندما ألقي نظرة على الكود وأرى تاريخ "UTC" لا أتذكر ما إذا كان في الواقع UTC أو مجرد UTC زائف ، خاصة عند إضافة مشكلة Chrome هناك. إنه يجعل من شبه المستحيل بالنسبة لي البرمجة. إنه يدمر ثقتي في قابلية قراءة الكود الخاص بي.

هذا هو سؤالي ، إذن ... بالعودة إلى المثال الأصلي الخاص بي ... في التوقيت العالمي المنسق (UTC) الحقيقي ، يشير 2018-11-01 19:00:00 إلى الساعة 1 مساءً بتوقيت أمريكا / دنفر (13:00). وفي المثال الخاص بي ، الساعة الواحدة ظهرًا بتوقيت أمريكا / دنفر هي الوقت الصحيح لحدثي. ولكن إذا لم يكن التوقيت العالمي المنسق (UTC) صحيحًا حسب التوقيت العالمي المنسق (UTC) ، فهل هذا يعني أنني يجب أن أمرر new Date(Date.UTC(2018, 10, 1, 13, 0, 0)) أو new Date(Date.UTC(2018, 10, 1, 19, 0, 0)) عند استخدام المعلمة tzid ؟ (الفرق هو 13:00 مقابل 19:00)

هل هو UTC الزائف يدخل ويخرج؟ أو التوقيت العالمي المتفق عليه (UTC) الحقيقي ولكن يظهر UTC الزائف؟ وكيف يمكنني أن أتذكر هذا بشكل موثوق ؟؟

قاعدتك محددة على النحو التالي:

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

ولكن وفقًا لـ RRULE RFC5545 ، يشير هذا إلى قاعدة في المنطقة الزمنية دنفر بالتوقيت المحلي لعام 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

إذا كنت تريد DTSTART في الساعة 1300 بتوقيت دنفر المحلي ، فيجب عليك تحديده على أنه 1300 في القاعدة الخاصة بك. يجب أن تتوافق المكتبة تمامًا مع المواصفات في هذا الصدد. (حسنًا ، باستثناء الحقيقة البسيطة المتمثلة في أنه سيعود 1300 بالتوقيت العالمي المنسق ...)

بالإضافة إلى ذلك ، تأكد من قراءة قسم "الوقت العائم":

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.

جافا سكريبت ، للأسف ، لا تقدم تنفيذ "الوقت العائم". أقرب خيار لدينا لوقت "خالص" بدون منطقة زمنية هو التوقيت العالمي المنسق (UTC) ، ومن ثم "psuedo-UTC". (للسجل ، يدعم rrule الأزمنة العائمة أيضًا ، ولا يتم تحديده مع TZID ولا مع مصمم UTC Z .)

بدأ التبديل إلى التوقيت العالمي المنسق بالكامل بهذا الالتزام: https://github.com/jakubroztocil/rrule/commit/850ed075175eb1acfcbd7b2cddf0606f2b2206f7

إذا قمت بإلقاء نظرة على الالتزام السابق ، واخترت الاختبار الإضافي من # 850ed075 ، فسترى أنه يفشل. في الواقع ، فشلت مئات الاختبارات ما لم يتم تشغيل المجموعة بأكملها بالتوقيت العالمي المنسق. هذا لأنه ، من أجل حساب التواريخ المستقبلية التي تقع عبر حدود التوقيت الصيفي ، كان علينا إنشاء مثيل للتواريخ عندما كانت الساعة الحالية تعمل بالتوقيت الصيفي ثم حساب الرياضيات التي تضعنا في التوقيت القياسي ، مما أدى إلى إزاحة غير صحيحة لمدة ساعة واحدة. يعطي التطبيق الحالي دائمًا الأوقات المحلية الصحيحة. الآن ، ستمر مجموعة الاختبار بأكملها في الواقع في أي بيئة محلية ، مع التنبيه على أنه للحصول على التاريخ / الساعة الصحيحة ، يجب عليك استخدام طرق getUTCDate() / getUTCHour() .

أنا أحقق في تغيير هذا في إصدار مستقبلي ، لكن التغيير ليس تافهًا دون اعتماد مكتبة طرف ثالث مطلوبة مثل Luxon.

في الإصدار القادم ، أود تغيير السلوك لإرجاع التواريخ الصحيحة دائمًا في منطقتك الزمنية المحلية ، على سبيل المثال:

  • سيعيد لك DTSTART;TZID=America/Denver:20181101T130000 تاريخًا يساوي 1300-0700 (التوقيت القياسي) إذا كنت في دنفر ، أو 2000Z إذا كنت تستخدم التوقيت العالمي المنسق (UTC)
  • يمنحك $ # DTSTART=20181101T130000 1300-0700 إذا كنت في دنفر ، و 1300Z إذا كنت تستخدم UTC
  • يمنحك $ DTSTART=20181101T130000Z 0600-0700 إذا كنت في دنفر ، و 1300Z إذا كنت تستخدم UTC

هل هذا هو السلوك الذي تتوقعه؟

تتمثل إحدى المشكلات الكبيرة في أن بيئة JS المحلية لا يمكنها تمثيل التواريخ إلا في منطقة زمنية واحدة. وبالتالي ، لا يكون new Date(...) ولا new Date(Date.UTC(...)) مناسبين حقًا عندما تريد تمثيل تاريخ ووقت في منطقة زمنية محددة (قد يختلف ذلك عن منطقتك الزمنية الحالية).

إذن ما هو السلوك المفترض أن يكون مع:

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

إذا كان الجهاز المحلي في America/Los_Angeles ؟ يجب أن تكون على دراية إلى حد ما بفارق ساعة واحدة بين الوقت الذي تنشئه والمنطقة الزمنية التي تريدها في ذلك الوقت. لذلك من المفترض أن تمثل الساعة 11 صباحًا (توقيت دنفر ، المنطقة المطلوبة) أو 10 صباحًا ( توقيت لوس أنجلوس ، المنطقة الأصلية)؟ أعتقد أنك يمكن أن تقدم قضية لأي منهما.

من الناحية التحذيرية ، يختلف الكائن Date عن كلٍ من سلسلة تاريخ ISO وسلسلة DTSTART ، حيث يمثل طابعًا زمنيًا بالمللي ثانية بالتوقيت العالمي المنسق والذي ليس له مفهوم المنطقة الزمنية أو الإزاحة. لذلك أعتقد أنه من المنطقي تمثيل الطابع الزمني للوقت المطلوب _ في المنطقة الصحيحة_ مما يعني أنك إذا كنت في لوس أنجلوس وتريد تمثيل الوقت في دنفر ، فيجب عليك تمرير كائن التاريخ الذي تمت تهيئته باستخدام النسبي تم تطبيق الإزاحة (لذا فإن المثال أعلاه يمثل الساعة 11 صباحًا في دنفر). هذا أمر محير ، لأنه إذا سافرت بعد ذلك إلى نيويورك وأردت تمثيل الوقت نفسه ، فعليك كتابة new Date(2018, 10, 1, 13, 0, 0) !

ميزة التخلص من المفهوم الكامل للتواريخ المحلية خارج النافذة هي أنك لست مضطرًا للتعامل مع هذا ؛ يمكن التعامل مع التاريخ والوقت المُمثلين كوقت عائم ، يمكن عرضه في أي منطقة زمنية دون إجراء أي تحويل.

هذا أمر معقد ، بالطبع ، بسبب سلوك محركات مثل Chrome.

لأغراضك ، يمكنك على الأرجح الحصول على القيم التي تريدها عن طريق القيام بما يلي:

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

أحاول حاليًا تحديد ما إذا كان هذا ينتمي إلى المكتبة المناسبة ...

شكرا لك! لقد أوضح ردك الأشياء بالنسبة لي حقًا. أعتقد أنني كنت متعبة ومرهقة أمس عندما كنت أحاول فهم كل شيء. أرى الآن ما يحدث ، وأنت على حق ، لقد كنت أمضي في الوقت الخطأ عند استخدام المعلمة tzid .

لأغراضك ، يمكنك على الأرجح الحصول على القيم التي تريدها عن طريق القيام بما يلي:

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

أحاول حاليًا تحديد ما إذا كان هذا ينتمي إلى المكتبة المناسبة ...

لقد نجح هذا بالنسبة لي! شكرا جزيلا. سيكون من الرائع لو كانت هناك طريقة لتكوين ما يتم إرجاعه بواسطة rule.all() و rule.between() لذلك لم نقدم تغييرًا فاصلًا آخر ، ولكن لا يزال بإمكاننا الحصول على السلوك أعلاه محليًا. لست متأكدًا من كيفية تحقيق ذلك عندما تفكر أيضًا في أنني أحب استخدام rrulestr() أو RRule.fromString() ولكن ربما تكون وسيلة إضافية أو شيء ما لتحديد نوع التاريخ الذي يتم إرجاعه؟


تحديث: يجب تغيير d.getUTCMilliseconds() أعلاه إلى d.getUTCSeconds()

لذلك ، يعمل الحل الخاص بك عندما تكون المنطقة الزمنية لنظامي هي نفس المنطقة الزمنية المطلوبة ، لكنني أمارس وقتًا عصيبًا في محاولة اكتشاف كيفية جعل RRule يعيد التواريخ في المنطقة الزمنية المطلوبة عندما يختلف عن المنطقة الزمنية للنظام. يقوم دائمًا بإرجاع التواريخ الممثلة في المنطقة الزمنية للنظام ، بدلاً من تمثيلها في المنطقة الزمنية المطلوبة. مثال:

  1. قم بتعيين المنطقة الزمنية لنظام التشغيل لديك على America/Denver
  2. في وحدة تحكم Chrome devtools ، أدخل:
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()
));

النتيجة صحيحة (لما أتوقعه / أحتاجه):

(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. قم بتغيير المنطقة الزمنية لنظام التشغيل إلى America/New_York
  2. قم بتشغيل نفس الأمر في وحدة تحكم Chrome devtools.

تختلف النتيجة لأنها ممثلة في المنطقة الزمنية للنظام:

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

لكني أريد أن تمثل النتيجة America/Denver time ، وليس المنطقة الزمنية لنظامي ، ولا تمثل التوقيت العالمي المنسق (UTC) الحقيقي. كيف يمكنني إعادة تقسيمه ليصبح America/Denver ؟ لست على دراية بـ Luxon ، لذا جربت 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());

لكن هذا يعيد نفس الشيء أثناء تواجده في نيويورك ، لأن toDate() يعيده على الفور إلى المنطقة الزمنية للنظام (في Chrome ، على الأقل).

shorlbeck إذا لاحظت ، فإن كلا من النتيجة في MDT و EDT هي نفسها في UTC ، لذا سؤالي هو: هل أنت متأكد من أنك تريد استخدام المعلمة TZID وليس الوقت العائم فقط؟ أوصي بالوقت العائم (بشكل أساسي هو نفسه ، ولكن مع DSTART:<datetime> بدلاً من DSTART;TZID=<timezone>:<datetime> ) إذا كنت تريد أن يُنشئ دائمًا 13:00 بغض النظر عن المنطقة الزمنية للنظام.

13:00 UTC الزائف أي. إذا كنت ترغب في التغلب على مشكلة Chrome ، فلا يزال يتعين عليك التحويل إلى تاريخ محلي باستخدام الطريقة الموجودة في تعليقي السابق.

davidgoli أوه ، أنت على حق. لم أكن أدرك أنني لست بحاجة فعليًا إلى معلمة tzid بعد أن فهمت كيف يعمل الوقت العائم / UTC الزائف بالفعل. وهذا يعني أنني لست بحاجة إلى الحل .map() أعلاه أيضًا. تعليقك وجهني في الاتجاه الصحيح. شكرا جزيلا.

سأكون كاذبًا إذا قلت أن هذا لم يكن الشيء الأكثر إرباكًا الذي عملت عليه منذ وقت طويل ، لكنني سعيد لأنني أسير في الاتجاه الصحيح الآن.

هل فكرت في استخدام تمثيل صريح لما تحتاجه (تواريخ عائمة بدون منطقة زمنية)؟ يعد استخدام تاريخ JS الأصلي مع المنطقة الزمنية UTC والادعاء بأنه UTC في المستندات أمرًا مربكًا للغاية لأنه يعمل نوعًا ما حتى يصيبك بأخطاء DST.

أود أن أقترح كائنًا ، دعنا نسميه "RRuleDate" الذي يأخذ مُنشئ (year, month, day, hours?, minutes?, seconds?, milliseconds?) وله طريقة .toDate() .

هذا تاريخ RRuleDate هذا هو ما يجب أن يعيده before() ، after() ، between() ، و all() ، والشيء الوحيد الذي يجب أن يعيده before() ، after() يجب أن يتم قبول الخيار between() و dtstart كمعلمات.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

espen picture espen  ·  10تعليقات

marcoancona picture marcoancona  ·  22تعليقات

maconfr picture maconfr  ·  6تعليقات

berardo picture berardo  ·  9تعليقات

grigio picture grigio  ·  7تعليقات