Rrule: DTSTART مع TZID: بين ينتج وقتًا غير صحيح

تم إنشاؤها على ١٦ يوليو ٢٠١٩  ·  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

عندما قمت بتعيين tzid تنفيذه بواسطة davidgoli أحصل على نتيجة مختلفة:

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

ما سبب ذلك؟
إصدار القاعدة: 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 (عندما أقوم بتعيين "env": { "TZ": "UTC" } في launch.json ). فشل الاختبار في أي منطقة زمنية أخرى.
davidgoli هل هذا مقصود وأنا tzid بشكل صحيح؟ أعتقد أنه تمت إضافته لإخبار rrule بالتشغيل في منطقة زمنية محددة.

لدي نفس المشكلة ولكن مع rrule.all (). يعمل التوقيت العالمي المنسق (UTC) ، بينما تطبق أي منطقة زمنية أخرى الإزاحة مرة أخرى عندما لا ينبغي ذلك.

أعتقد أنه قد يكون له علاقة بالسطر 1849 من rrule-tz.js.

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded ، خلافًا لاسمه ، بإجراء أي عمليات فحص ودائمًا ما يتم إعادة تحديد المناطق ما لم يكن التوقيت العالمي المنسق (UTC).

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

يبدو أنه يعيد تقسيم التاريخ على الرغم من أنه قد تم تحديده بشكل صحيح بالفعل.

يبدو أن تغيير rezonedDate() لإرجاع this.date يُصلح الأمور بالنسبة لي. لا يمكنني التأكد تمامًا من أنني كسرت شيئًا ما في مكان آخر ، ولكن من نظرة خاطفة ، يبدو أن كل شيء آخر يعمل بشكل جيد عند إجراء هذا التغيير.

أعتقد أنه قد يكون له علاقة بالسطر 1849 من rrule-tz.js.

var rezonedDate = rezoneIfNeeded(res, options);

rezoneIfNeeded ، خلافًا لاسمه ، بإجراء أي عمليات فحص ودائمًا ما يتم إعادة تحديد المناطق ما لم يكن التوقيت العالمي المنسق (UTC).

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

يبدو أنه يعيد تقسيم التاريخ على الرغم من أنه قد تم تحديده بشكل صحيح بالفعل.

هذا غير صحيح. تتحقق طريقة rezonedDate إذا تم تعيين tzid لتحديد ما إذا كان سيتم تطبيق إزاحة المنطقة أم لا.

agordeev سأبحث في هذه المسألة. لم يكن "إصلاح" tzid في كلتا الحالتين؟

بغض النظر ، فإن التناقض بين سلوك RRule و RRuleSet يثير قلقي وربما خطأ يحتاج إلى نظرة أعمق. شكرا على حالة الاختبار!

أرى المشكلة التي أدت إلى مشكلتي - لم يكن رمز الخادم الخاص بي يتعامل مع وقت قيمة DTSTART بشكل صحيح ، ويمر بالتوقيت العالمي المنسق (UTC) عندما يجب ترجمته إلى المنطقة الزمنية المحددة بواسطة TZID. ربما يساعدك ذلك في حل مشكلتك davidgoli لتوجيهي في الاتجاه الصحيح.

يبدو أنني قد عثرت على مشكلة في قاعدة البيانات. طريقة toString() في datewithzone.ts ستخرج وقت UTC عندما يتم توفير TZID ، عندما يجب أن ينتج الوقت المترجم بدلاً من ذلك.

@ hlee5zebra تأكد من ملاحظة هذا النص في الملف التمهيدي:

https://github.com/jakubroztocil/rrule#important -use-utc- التواريخ

هام: استخدم التواريخ UTC

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

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

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

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

يوجد التجعد الإضافي في معظم تطبيقات JS ، إما أن تحصل على تعويض 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.all() قليلاً لمحاولة جعل هذه المشكلة قابلة للتكرار ، وإليك ما وجدته لإعادة إظهار هذه المشكلة بشكل موثوق:

  1. لقد حاولت ضبط DTSTART على منتصف الليل ضمن المناطق الزمنية الخاصة بكل من "America / Adak" و "America / Chicago" (وقتي المحلي) و "America / New_York" و "UTC" ، والنتيجة 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"

التوقيت العالمي:
"2019-07-18T00: 00: 00.000Z"

يبدو أنه عندما لا يتم تعيين المنطقة الزمنية على UTC (مثل America / Adak، America / New_York) ، فسيتم طرح الإزاحة بين التوقيت المحلي والمنطقة الزمنية المحددة من تاريخ DTSTART. لذا فإن سلسلة New York ISO تظهر الساعة 23:00 منذ الإزاحة بين التوقيت المحلي ونيويورك هي +1 ، والتي عند طرحها من منتصف الليل في 07/18/2019 ، ينتج عنها ما نراه ، وهو 11 مساءً في 07/17 / 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 هي الطريقة الصحيحة التي أوصى بها التمهيدي.

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

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

agordeev سأبحث في هذه المسألة. لم يكن "إصلاح"

شكرا لردك ديفيد.

    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. تعتبر القاعدة DTSTART في UTC عندما أحذف معلمة tzid.

إذا كنت تريد دائمًا أن تكون التكرارات وفقًا للمنطقة الزمنية المحلية للمستخدم ، فلا يجب عليك استخدام tzid.

اعتقدت أن الخيار الوحيد للحصول على التكرارات في المنطقة الزمنية للمستخدم هو تمرير تلك المنطقة الزمنية إلى معلمة tzid؟ مع الأخذ في الاعتبار أن المكتبة تستخدم مع node.js على الخادم.

إذا كان التكرار سيكون عند 1800 بالتوقيت المحلي للمستخدم _ بغض النظر عن المنطقة الزمنية_ ، فلن تحتاج إلى استخدام tzid . يرجى التأكد من أنك قرأت عن الأوقات "العائمة" كما هو موضح في README: https://github.com/jakubroztocil/rrule#important -use-utc-date

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

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

elazar picture elazar  ·  18تعليقات

Prinzhorn picture Prinzhorn  ·  15تعليقات

zeluspudding picture zeluspudding  ·  11تعليقات

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

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