Rrule: ν•˜λ£¨ λ‹¨μœ„λ‘œ μš”μΌ μ‹œν”„νŠΈλ₯Ό μ‚¬μš©ν•˜λŠ” 반볡 이벀트

에 λ§Œλ“  2020λ…„ 03μ›” 22일  Β·  3μ½”λ©˜νŠΈ  Β·  좜처: jakubroztocil/rrule

  • [x] 이전에 쀑볡에 λŒ€ν•œ κΈ°μ‘΄ 문제λ₯Ό μ‚΄νŽ΄λ³΄μ•˜λŠ”μ§€ ν™•μΈν•˜μ‹­μ‹œμ˜€.
    μƒˆλ‘œ λ§Œλ“€κΈ°
  • [x] 문제λ₯Ό μž¬ν˜„ν•˜λŠ” μ½”λ“œ μƒ˜ν”Œ. λͺ¨λ“  μž…λ ₯ 값을 포함해야 ν•©λ‹ˆλ‹€.
    μ •ν™•ν•œ RRule λ¬Έμžμ—΄ 및 λ‚ μ§œμ™€ 같은 것을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
  • [x] μ˜ˆμƒ 좜λ ₯
  • [x] μ‹€μ œ 좜λ ₯
  • [x] μ‚¬μš© 쀑인 rrule 버전

    • 2.6.4

  • [x] 운영 체제

    • λ¦¬λˆ…μŠ€

  • [x] ν˜„μ§€ μ‹œκ°„λŒ€(버그λ₯Ό ν‘œμ‹œν•˜λŠ” μ‹œμŠ€ν…œμ˜ λͺ…λ Ήμ€„μ—μ„œ $ date μ‹€ν–‰)

    • 유럽/νŒŒλ¦¬μ΄μ§€λ§Œ μ‹œκ°„λŒ€κ°€ μ΄λ™ν•©λ‹ˆλ‹€. μ•„λž˜ μ°Έμ‘°


주어진 λ‚ μ§œ(유럽/νŒŒλ¦¬μ—μ„œ 2020λ…„ 2μ›” 1일) 이후에 λ‹€μŒ ν™”μš”μΌμ„ μ–»μœΌλ €κ³  ν•©λ‹ˆλ‹€.
κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  μ΄λ²€νŠΈλŠ” μ—¬μ „νžˆ ν•΄λ‹Ή μ‹œκ°„λŒ€μ— μžˆλŠ” 경우 μˆ˜μš”μΌμ— μ‹œμž‘λ©λ‹ˆλ‹€.
America/New_York μ‹œκ°„λŒ€ μ΄λ²€νŠΈμ— λŒ€ν•œ 이벀트 계산은 μ˜ˆμƒλŒ€λ‘œμž…λ‹ˆλ‹€.

μ•„λž˜ μŠ€λ‹ˆνŽ«μ„ ν™•μΈν•˜μ„Έμš”. λ‚΄ 연ꡬ κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

// problem: events should be on Tuesday but are shifted by one day:
// Tue Feb 04 2020 00:00:00 GMT+0100 (Central European Standard Time) ...
$ TZ=Europe/Paris node zone.js Europe/Paris
rule:
DTSTART;TZID=Europe/Paris:20200131T230000
RRULE:FREQ=WEEKLY;BYDAY=TU;COUNT=2
events:
Wed Feb 05 2020 00:00:00 GMT+0100 (Central European Standard Time)
Wed Feb 12 2020 00:00:00 GMT+0100 (Central European Standard Time)
luxon:
Sat Feb 01 2020 00:00:00 GMT+0100 (Central European Standard Time)

// timezone America/New_York shows correct result
$ TZ=America/New_York node zone.js America/New_York
rule:
DTSTART;TZID=America/New_York:20200201T050000
RRULE:FREQ=WEEKLY;BYDAY=TU;COUNT=2
events:
Tue Feb 04 2020 00:00:00 GMT-0500 (Eastern Standard Time)
Tue Feb 11 2020 00:00:00 GMT-0500 (Eastern Standard Time)
luxon:
Sat Feb 01 2020 00:00:00 GMT-0500 (Eastern Standard Time)

// crossing timezones show correct result
$ TZ=America/New_York node zone.js Europe/Paris
rule:
DTSTART;TZID=Europe/Paris:20200201T050000
RRULE:FREQ=WEEKLY;BYDAY=TU;COUNT=2
events:
Mon Feb 03 2020 18:00:00 GMT-0500 (Eastern Standard Time)
Mon Feb 10 2020 18:00:00 GMT-0500 (Eastern Standard Time)
luxon:
Sat Feb 01 2020 00:00:00 GMT-0500 (Eastern Standard Time)

// UTC shows correct result
$ TZ=UTC node zone.js UTC
rule:
DTSTART:20200201T000000Z
RRULE:FREQ=WEEKLY;BYDAY=TU;COUNT=2
events:
Tue Feb 04 2020 00:00:00 GMT+0000 (Coordinated Universal Time)
Tue Feb 11 2020 00:00:00 GMT+0000 (Coordinated Universal Time)
luxon:
Sat Feb 01 2020 00:00:00 GMT+0000 (Coordinated Universal Time)
const { DateTime } = require('luxon')
const { RRule } = require('rrule/dist/es5/rrule-tz.js')

const dtstart = new Date(2020, 1, 1, 0)
const tzid = process.argv[2] || 'Europe/Paris'

const rule = new RRule({
  freq: RRule.WEEKLY,
  dtstart,
  tzid,
  byweekday: [RRule.TU],
  count: 2
})

console.log('rule:\n' + rule.toString())
console.log('events:\n' + rule.all().map(d => d.toString()).join('\n'))

const datetime = DateTime.fromJSDate(dtstart).setZone(tzid)
console.log('luxon:\n' + datetime.toJSDate().toString())

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

μ•ˆλ…•ν•˜μ„Έμš”, 여기에 같은 문제
반볡: "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;UNTIL=20200430T040000Z"

rule.all() λŠ” TU,WE,TH,FR,SA λ³΄μ—¬μ€λ‹ˆλ‹€.

λͺ¨λ“  3 λŒ“κΈ€

μ•ˆλ…•ν•˜μ„Έμš”, 여기에 같은 문제
반볡: "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;UNTIL=20200430T040000Z"

rule.all() λŠ” TU,WE,TH,FR,SA λ³΄μ—¬μ€λ‹ˆλ‹€.

μ—…λ°μ΄νŠΈ: λ‚˜λŠ” 이것이 μˆœμ „νžˆ UTC μž…λ ₯ 및 JS Date 객체에 κ΄€ν•œ rrule.js의 κΈ°λ³Έ λ™μž‘μ— λŒ€ν•œ λ‚΄ ν˜Όλž€μ΄λΌκ³  μƒκ°ν•©λ‹ˆλ‹€.

λ‹€λ₯Έ μ˜κ²¬μ—μ„œ μ•Œ 수 μžˆλ“―μ΄ 이 ν˜Όλ™μ€ 맀우 ν”ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€.
(1) TZID둜 μž‘μ—…ν•˜κΈ°
(2) DTSTARTλ₯Ό μ˜¬λ°”λ₯Έ ν˜•μ‹μœΌλ‘œ μž…λ ₯ν•©λ‹ˆλ‹€(UTC λ˜λŠ” 컴퓨터에 둜컬이 μ•„λ‹Œ μ‹œκ°„λŒ€μ— 둜컬).
(3) rrule.between() μ •ν™•ν•œ λ‚ μ§œλ₯Ό μž…λ ₯ν•©λ‹ˆλ‹€. TZIDλ₯Ό μ„ μ–Έ ν•  경우, λ‚ μ§œλ₯Ό λ§ˆμ‚¬μ§€κ°€ ν›¨μ”¬μ²˜λŸΌ 그듀은 λ‚˜μ˜¬ λ•Œ - μ‚¬μš© DateTime.fromJSDate(date).toUTC().setZone('local', { keepLocalTime: true }) , λ‹Ήμ‹ μ€μ—κ°€λŠ” λ‚ μ§œλ₯Ό λ§ˆμ‚¬μ§€ν•΄μ•Ό between() (κ°€λŠ₯성이 after/before 둜 음) DateTime.fromMillis(posixTime).setZone('utc', { keepLocalTime: true }) .

λ¬Έμ„œν™” λ¬Έμ œμ™€ λ¬Έμ„œν™”λ˜μ§€ μ•Šμ€ λ™μž‘μ— λŒ€ν•œ λΆˆλ§Œμ—λ„ λΆˆκ΅¬ν•˜κ³  이 λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μžˆλ‹€λŠ” 사싀에 맀우 κ°μ‚¬ν•©λ‹ˆλ‹€. μ œμž‘μžμ™€ λͺ¨λ“  μœ μ§€ κ΄€λ¦¬μž 및 κΈ°μ—¬μžμ—κ²Œ κ°μ‚¬ν•©λ‹ˆλ‹€. luxon κ°€ moment μ—μ„œ 배운 κ΅ν›ˆμ„ λ°”νƒ•μœΌλ‘œ μ„±μž₯ν•œ κ²ƒμ²˜λŸΌ λ‹€μŒ rule 라이브러리 λ°˜λ³΅μ€ rrule.js 의 μ–΄κΉ¨ μœ„μ— ꡬ좕될 것이라고 μƒκ°ν•©λ‹ˆλ‹€.

μ›λž˜ κ²Œμ‹œλœ "문제", μ•„λž˜μ— λ§ν•œ λ‚΄μš©μ€ λ¬΄μ‹œν•˜μ‹­μ‹œμ˜€.
λ˜ν•œ 이 문제둜 λ³΄μ΄λŠ” ν˜„μƒλ„ λ°œμƒν•©λ‹ˆλ‹€.

import { rrulestr } from 'rrule';

const twoWeeks = 1209600000;
const myrrule = ["DTSTART:20200104T000000Z", "RRULE:FREQ=WEEKLY;BYDAY=FR"]
rrulestr(myrrule.join('\n'))
    .between(new Date(Date.now() - twoWeeks), new Date(Date.now() + twoWeeks))

λͺ©μš”일에 λ°œμƒν•˜λŠ” λ‚ μ§œ λͺ©λ‘μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€.
DTSTARTκ°€ UTC인지 ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€.

ν•΄λ‹Ή μ΄λ²€νŠΈλŠ” κΈˆμš”μΌ 18:00-0600(μ‚°μ•… μ‹œκ°„)에 λ°œμƒν•˜λ©° μ΄λŠ” ν† μš”μΌ 00:00-UTC와 λ™μΌν•˜μ§€λ§Œ rrule.between() λŠ” λͺ©μš”일 18:00-0600에 μ‹œκ°„μ„ μ΄μƒν•˜κ²Œ λ°˜ν™˜ν•©λ‹ˆλ‹€.

λ¬Έμ œκ°€ μ–΄λ””μ„œ/μ–΄λ–»κ²Œ λ°œμƒν•˜λŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ 기꺼이 μ½”λ“œλ₯Ό 뢄석해 λ³΄κ² μŠ΅λ‹ˆλ‹€. μ½”λ“œλ² μ΄μŠ€μ— λŒ€ν•΄ 더 잘 μ•Œκ³  μžˆλŠ” μ‚¬λžŒμ΄ μ˜¬λ°”λ₯Έ λ°©ν–₯으둜 λ‚˜λ₯Ό κ°€λ¦¬ν‚€λŠ” λͺ‡ 가지 팁이 μžˆλ‹€λ©΄ μ•Œλ €μ£Όμ„Έμš”. κ°μ‚¬ν•©λ‹ˆλ‹€!

@davidgoli μ•ˆλ…•ν•˜μ„Έμš” λ°μ΄λΉ„λ“œ! λ‚˜λŠ” μ§€λ‚œ 5일 λ™μ•ˆ rrule둜 μž‘μ—…ν•΄ μ™”μœΌλ©° μ‹œκ°„λŒ€ μž‘μ—… 방법을 μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€. μ˜€ν›„ 9μ‹œμ— μ‹œκ°„μ„ μž…λ ₯ν•˜λ©΄ UTC μ‹œκ°„μœΌλ‘œ μ˜€μ „ 4μ‹œλ‘œ λ³€ν™˜λ©λ‹ˆλ‹€. 이둜 인해 νŠΉμ • μš”μΌ(예: κΈˆμš”μΌ)에 λ°˜λ³΅λ˜λŠ” 이벀트λ₯Ό μ„€μ •ν•˜λ €λŠ” 경우 λ¬Έμ œκ°€ λ°œμƒν•©λ‹ˆλ‹€. 그런 λ‹€μŒ κΈˆμš”μΌ μ˜€μ „ 4μ‹œλ₯Ό λ°˜λ³΅ν•˜κ³  λ‚΄ μ‹œκ°„λŒ€λ‘œ λ‹€μ‹œ λ²ˆμ—­λ˜λ©΄ κΈˆμš”μΌμ— μ›ν–ˆλ˜ λͺ©μš”일 μ˜€ν›„ 9μ‹œμž…λ‹ˆλ‹€. 이에 λŒ€ν•œ μ œμ•ˆ 사항이 μžˆμŠ΅λ‹ˆκΉŒ? tzid둜 μ‹œλ„ν–ˆμ§€λ§Œ μž‘λ™ν•˜λ„λ‘ ν•  수 μ—†μŠ΅λ‹ˆλ‹€. Luxon을 ν†΅ν•©ν•˜λŠ” 방법을 잘 λͺ¨λ₯΄μ‹­λ‹ˆκΉŒ? νŒ¨ν‚€μ§€λ₯Ό λ‹€μš΄λ‘œλ“œν–ˆμŠ΅λ‹ˆλ‹€.

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰