Rrule: "์‹œ๊ฐ„๋Œ€ ์ง€์›"์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ ๋ณ€๊ฒฝ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์Œ

์— ๋งŒ๋“  2018๋…„ 11์›” 20์ผ  ยท  21์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: jakubroztocil/rrule

ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(v2.5.6)์˜ ๋™์ž‘์„ ๋ฐฑ์—”๋“œ์—์„œ ๋™๋“ฑํ•œ PHP ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (v2.3.3)์™€ ๋น„๊ตํ–ˆ์Šต๋‹ˆ๋‹ค. ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ ๋ณ€๊ฒฝ์ด ์ฒ˜๋ฆฌ๋˜๋Š” ๋ฐฉ์‹์—๋Š” ๋ถ„๋ช…ํ•œ ๋ถˆ์ผ์น˜๊ฐ€ ์žˆ์œผ๋ฉฐ PHP ๋ฒ„์ „์ด ์ด๋ฅผ ๋” ์ž˜ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ์˜ˆ:

America/Denver ์‹œ๊ฐ„๋Œ€์—์„œ 2018๋…„ 11์›” 4์ผ ์ผ์š”์ผ์— ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค(GMT-6์—์„œ GMT-7๋กœ ๋ณ€๊ฒฝ). ๋”ฐ๋ผ์„œ ์˜คํ›„ 1์‹œ์— ์‹œ์ž‘ํ•˜์—ฌ ์‹œ๊ฐ„ ์ „ํ™˜ ์ „์ธ 11์›” 1์ผ๋ถ€ํ„ฐ ๋งค์ฃผ ์›”, ์ˆ˜, ๋ชฉ์— ๋ฐ˜๋ณต๋˜๋Š” ๋ฐ˜๋ณต ์‹œ๋ฆฌ์ฆˆ๋ฅผ ์„ค์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

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

์ด ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ชจ๋“  ๋ฐ˜๋ณต์ด ์˜คํ›„ 1์‹œ์— ์‹œ์ž‘๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค. (๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด 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 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋น„์Šทํ•œ ์ƒํ™ฉ์„ ์„ค์ •ํ–ˆ์„ ๋•Œ ๊ฒฐ๊ณผ๋Š” ์œ„์—์„œ ์˜ˆ์ƒํ•œ ๋Œ€๋กœ์˜€์œผ๋ฉฐ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค ๋Š” UTC๊ฐ€ ์•„๋‹Œ America/Denver ์‹œ๊ฐ„๋Œ€์—์„œ 13:00:00์— ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐํƒ€ ์„ธ๋ถ€ ์‚ฌํ•ญ:

  • ๋ฒ„์ „ 2.5.6
  • ๋งฅ OS X 10.13.6
  • ํฌ๋กฌ 70
  • ํ˜„์žฌ ํ˜„์ง€ ์‹œ๊ฐ„์€ MST(๋ฏธ๊ตญ/๋ด๋ฒ„, GMT-7)์ž…๋‹ˆ๋‹ค.

ํŽธ์ง‘: ์˜คํƒ€ ์ˆ˜์ •

๋ชจ๋“  21 ๋Œ“๊ธ€

์ฝ˜์†” ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๊นŒ? ์˜ˆ:

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

Luxon์€ optionalDependencies ์— ์žˆ์œผ๋ฏ€๋กœ node_modules ์— ์žˆ์œผ๋ฉด ๊ฐ€์ ธ์˜ค๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

rrule ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ์„ ์–ป์Šต๋‹ˆ๋‹ค.

      [
        [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์‹œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ์ƒํ•œ ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค. (์ด๊ฒƒ์€ PHP๊ฐ€ ์•„๋‹ˆ๋ผ JavaScript์ด๊ธฐ ๋•Œ๋ฌธ์— ๋กœ์ปฌ ๋จธ์‹  ์‹œ๊ฐ„๋Œ€ ๋˜๋Š” UTC ์ด์™ธ์˜ ์‹œ๊ฐ„๋Œ€์—์„œ๋Š” Date ๊ฐœ์ฒด๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ UTC๋Š” ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•ญ์ƒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.)

UTC ๋‚ ์งœ๊ฐ€ ์•„๋‹Œ ํ˜„์ง€(MST/MDT) ๋‚ ์งœ๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค๋Š” ์‚ฌ์‹ค์ด ์•ฝ๊ฐ„ ๋‹นํ™ฉ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์•„๋Š” ํ•œ, ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ˜„์žฌ UTC ๋‚ ์งœ๋งŒ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ตฌ์„ฑ๋˜์–ด ์žˆ์ง€๋งŒ ์ด์ „์—๋Š” ๋Œ€์‹  ํ˜„์ง€ ๋‚ ์งœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ฝ˜์†” ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๊นŒ? ์˜ˆ:

'์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Luxon ์—†์ด TZID๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋œ ์‹œ๊ฐ„์€ ์š”์ฒญํ•œ ์‹œ๊ฐ„๋Œ€๊ฐ€ ์•„๋‹ˆ๋ผ UTC์ž…๋‹ˆ๋‹ค'

์ฝ˜์†” ๊ฒฝ๊ณ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Luxon์ด ์ œ๋Œ€๋กœ ์ œ๊ณต๋˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์‹œ๊ฐ„๋Œ€์— ๊ด€ํ•ด์„œ๋Š” Chrome devtools ์ฝ˜์†” ๋ช…๋ น์ค„์—์„œ ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ MST/DST๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋‹จ๊ณ„: Chrome ์—ด๊ธฐ --> RRule ๋ฐ Luxon์ด ์ œ๊ณต๋œ ํŽ˜์ด์ง€๋กœ ์ด๋™ --> Cmd+Opt+I --> "์ฝ˜์†”" ํƒญ --> ์œ„ ์Šค๋‹ˆํŽซ์„ ๋ณต์‚ฌํ•˜์—ฌ ๋งจ ์•„๋ž˜์˜ ๋ช…๋ น์ค„์— ๋ถ™์—ฌ๋„ฃ๊ธฐ --> Enter

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ˜„์žฌ UTC ๋‚ ์งœ๋งŒ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ตฌ์„ฑ๋˜์–ด ์žˆ์ง€๋งŒ ์ด์ „์—๋Š” ๋Œ€์‹  ํ˜„์ง€ ๋‚ ์งœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒŒ ์–ด๋Š ๋ฒ„์ „์—์„œ ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ์•„์„ธ์š”? ์™œ ๊ทธ๋Ÿฐ ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚˜๊ฒ ์Šต๋‹ˆ๊นŒ? UTC ๋‚ ์งœ ๋งŒ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์‹ค์ œ๋กœ ์ด ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ ๋ถˆ์ผ์น˜ ๊ฐ€ ๋ฐœ์ƒ ํ•ฉ๋‹ˆ๋‹ค! ํ•ญ์ƒ 13:00 UTC์— ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋Š” ์ด๋ฒคํŠธ๊ฐ€ ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ ์ „ํ™˜ ์ „/ํ›„ ๋‹ค๋ฅธ ์‹œ๊ฐ„์— ์‹œ์ž‘๋˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋˜๋Œ๋ฆฌ๊ฑฐ๋‚˜ ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ๋Œ€์ฒด ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. UTC๋Š” ๋งค์šฐ ์œ ์šฉํ•˜์ง€๋งŒ ๋ชจ๋“  ์ƒํ™ฉ์—์„œ ํ•ญ์ƒ ์˜ฌ๋ฐ”๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์•„๋‹™๋‹ˆ๋‹ค. ๋˜ํ’€์ด๋˜๋Š” ์ด๋ฒคํŠธ๋กœ ์ž‘์—…ํ•  ๋•Œ๋Š” ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ๊ฐ€ ์ ์šฉ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ํŠน์ • ์‹œ๊ฐ„๋Œ€๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ Node.js์—์„œ์™€ Chrome์—์„œ ๋‹ค๋ฅธ ๋™์ž‘์„ ์–ป์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‹น์‹ ์˜ ์ฃผ์–ด์ง„ "์˜ˆ์ƒ"์ถœ๋ ฅ์ด ์™œ ๋‹น์‹ ์ด ์ •๋ง๋กœ ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฒƒ์ธ์ง€ ์ดํ•ดํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์‹œ๊ฐ„์ด ์ž˜๋ชป ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์˜ฌ๋ฐ”๋ฅธ ๊ฒƒ์€_ ์ฃผ์–ด์ง„ ํ˜„์ง€ ์‹œ๊ฐ„์— ๊ฐ ์‹œ๊ฐ„์— ๋Œ€ํ•œ 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์€ 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์€ Luxon์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์ง€ ์‹œ๊ฐ„๋Œ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ _์ „ ์„ธ๊ณ„์˜ ๋ชจ๋“  ์‹œ๊ฐ„๋Œ€_์— ๋Œ€ํ•œ ์ •ํ™•ํ•œ ํ˜„์ง€ ์‹œ๊ฐ„์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด UTC ์‚ฌ์šฉ์ด ์ค‘์š”ํ•œ ์ด์œ ์ž…๋‹ˆ๋‹ค. ์‹œ๊ฐ„๋Œ€ ์ฝ”๋“œ๊ฐ€ ํ˜„์ง€ ์‹œ๊ฐ„๋Œ€์— ๋Œ€ํ•ด "์˜ฌ๋ฐ”๋ฅธ" ๋‚ ์งœ๋ฅผ ๋ฐ˜ํ™˜ํ–ˆ์ง€๋งŒ ๋‹ค๋ฅธ ์‹œ๊ฐ„๋Œ€( TZID ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ํ•จ๊ป˜ ์ œ๊ณต๋จ)์—์„œ ๋‚ ์งœ๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—„์ฒญ๋‚˜๊ฒŒ ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ. ์šฐ๋ฆฌ๊ฐ€ "UTC ๋‚ ์งœ"๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” UTC๋กœ DST ์ˆ˜ํ•™์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด ์•„๋‹ˆ๋ผ(ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ  ์˜ฌ๋ฐ”๋ฅธ DST ๊ณ„์‚ฐ์˜ ๋งŽ์€ ์˜ˆ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค) JavaScript ์‚ฌ์–‘์˜ ์‹œ๊ฐ„๋Œ€๊ฐ€ ๋”์ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊นจ์ง€๊ณ  UTC๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ "์ค‘๋ฆฝ์ ์ธ" ๋‚ ์งœ ๊ฐœ๋…์— ๊ฐ€์žฅ ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. ์ž‘์—…ํ•˜๊ธฐ ํ˜ผ๋ž€์Šค๋Ÿฝ๊ณ  ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์‚ฌ์šฉ๋ฒ•์ด ๋” ๋ช…ํ™•ํ•˜์ง€ ์•Š๋‹ค๋Š” ์ ์— ๋Œ€ํ•ด ์œ ๊ฐ์Šค๋Ÿฝ๊ฒŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค(์ œ์•ˆ์€ ์—ด๋ ค ์žˆ์Šต๋‹ˆ๋‹ค).

์šฐ๋ ค๋˜๋Š” ๊ฒƒ์€ ๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ด์ œ ์‹คํ–‰ ์ค‘์ธ Node ์ฝ˜์†”์ด๋‚˜ Firefox์—์„œ ๊ณ ์œ ํ•œ ์ƒ˜ํ”Œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋งค์šฐ ๋‹ค๋ฅธ(๊ทธ๋ฆฌ๊ณ  ์ •ํ™•ํ•œ) ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์—ฌ๊ธฐ์„œ ์‹ค์ œ ๋ฌธ์ œ๋Š” Chrome์—์„œ ๊ฒฐ๊ณผ๊ฐ€ ํ˜ผ๋™๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ •ํ™•ํžˆ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋Š”์ง€ ๋‹น์žฅ์€ ์•Œ ์ˆ˜ ์—†์ง€๋งŒ ์–ธ๊ธ‰ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๋‘ ํ™˜๊ฒฝ ๋ชจ๋‘ ์ •ํ™•ํžˆ ๋™์ผํ•œ ISO ๋ฌธ์ž์—ด๊ณผ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ์ƒ์„ฑํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@davidgoli ๋งž์Šต๋‹ˆ๋‹ค. Firefox๋Š” ์›๋ž˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์ฝ˜์†”์— ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฒฐ๊ณผ๋Š” ๊ถ๊ทน์ ์œผ๋กœ ๋™์ผํ•œ ์‹œ๊ฐ„์ด๋ฉฐ Firefox์—์„œ๋Š” ๊ฒฐ๊ณผ๊ฐ€ UTC ์‹œ๊ฐ„์œผ๋กœ ํ‘œ์‹œ๋˜๋Š” ๋ฐ˜๋ฉด Chrome์—์„œ๋Š” America/Denver ์‹œ๊ฐ„(๋˜๋Š” ์•„๋งˆ๋„ ํ˜„์ง€ ์‹œ๊ฐ„๋Œ€๊ฐ€ ๋ฌด์—‡์ด๋“ )์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‹น์‹ ์˜ ์ฃผ์–ด์ง„ "์˜ˆ์ƒ"์ถœ๋ ฅ์ด ์™œ ๋‹น์‹ ์ด ์ •๋ง๋กœ ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฒƒ์ธ์ง€ ์ดํ•ดํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์‹œ๊ฐ„์ด ์ž˜๋ชป ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ฃผ์–ด์ง„ ํ˜„์ง€ ์‹œ๊ฐ„์— ๊ฐ ์‹œ๊ฐ„์— ๋Œ€ํ•œ TZ ์˜คํ”„์…‹์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ •ํ™•ํ•œ ์‹œ๊ฐ„์ด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ชจ๋“  ์‹œ๊ฐ„์€ 19:00์ด ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด RRule์ด UTC๋ฅผ ๋‹ค๋ฃจ๋Š” ์ด์œ ์ด๋ฉฐ UTC์˜ ์‚ฌ์šฉ์ด ์ด ํŠน์ • ๋ฒ„๊ทธ์™€ ์•„๋ฌด ๊ด€๋ จ์ด ์—†๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  ๋ฐ˜๋ณต ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด 19:00 UTC ์‹œ๊ฐ„์„ ์›ํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๋งค์ฃผ ์ˆ˜์š”์ผ ์˜คํ›„ 1์‹œ์— America/Denver ์‹œ๊ฐ„๋Œ€ ์˜ ํŠน์ • ์œ„์น˜์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์˜ˆ์•ฝํ•˜๋ ค๊ณ  ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ, ์—ฐ์ค‘ ์–ธ์ œ์ด๊ฑด ์ƒ๊ด€์—†์ด ์‚ฌ์šฉ์ž๋Š” ๋งค์ฃผ ์ˆ˜์š”์ผ ์˜คํ›„ 1์‹œ ์— ํ•ด๋‹น ์œ„์น˜์—์„œ ์ˆ˜์š”์ผ ์˜คํ›„ 1์‹œ์— ๋™์ผํ•œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. 12์‹œ๋„ ์•„๋‹ˆ๊ณ  2์‹œ๋„ ์•„๋‹ˆ๊ณ  ๋ฏธ๊ตญ/๋ด๋ฒ„ ์‹œ๊ฐ„์˜ ๊ฒฝ์šฐ ํ•ญ์ƒ ์˜คํ›„ 1์‹œ์— ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ rrule์€ timezone์„ ๋„˜๊ฒจ๋„ UTC ์‹œ๊ฐ„์— ๋”ฐ๋ผ ์žฌ๋ฐœ์„ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ "์˜ค์˜ค์˜คํ›„ 1์‹œ America/Denver๋Š” 19:00 ์œผ๋กœ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์€ ์‚ฌ์‹ค์ด ์•„๋‹™๋‹ˆ๋‹ค. 2018๋…„ 11์›” 4์ผ ์ด์ „์€ ์‚ฌ์‹ค์ด์ง€๋งŒ 11์›” 4์ผ ์ดํ›„ America/Denver๋Š” ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ๋ฅผ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์ ์—์„œ UTC ์‹œ๊ฐ„ 19:00 ์€ ์ด์ œ America/Denver ์‹œ๊ฐ„ 12:00pm์œผ๋กœ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค . ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž๋Š” ์ด๋ฒคํŠธ๊ฐ€ ์˜คํ›„ 1์‹œ๊ฐ€ ์•„๋‹ˆ๋ผ ์˜คํ›„ 12์‹œ์— ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ์ž˜๋ชป ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, 1pm America/Denver๋Š” ์ผ๊ด‘ ์ ˆ์•ฝ ์‹œ๊ฐ„์ œ์ธ์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ 19:00 ๋˜๋Š” 20:00 UTC๊ฐ€ ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ 19:00๋ผ๊ณ  ๋งํ•˜๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์— ์˜ฌ๋ฐ”๋ฅธ ๋ฐ˜๋ณต์„ ์–ป๊ธฐ ์œ„ํ•ด rrule์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. 00์€ ์—ฐ์ค‘ ๋‚ด๋‚ด ์ •ํ™•ํ•œ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” rrule์ด ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋ฏฟ๋Š” ์‹ ํ™”, ์ฆ‰ UTC๊ฐ€ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ ๋‹ค๋ฃฐ ๋•Œ ํ•ญ์ƒ ์˜ฌ๋ฐ”๋ฅธ ์†”๋ฃจ์…˜์ด๋ผ๋Š” ์‹ ํ™”๋ฅผ ๋ฐ›์•„๋“ค์ด๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์ด ์ตœ์„ ์˜ ์†”๋ฃจ์…˜์ด ์•„๋‹Œ ํŠน์ • ๊ฒฝ์šฐ๊ฐ€ ์žˆ์œผ๋ฉฐ ์ด ๊ฒฝ์šฐ(ํŠน์ • ์œ„์น˜์—์„œ ๋ฏธ๋ž˜์˜ ์ด๋ฒคํŠธ ๋ฐ˜๋ณต)๊ฐ€ ๊ทธ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.


ํŽธ์ง‘: ์ด๋ก ์ ์œผ๋กœ rrule์ด UTC๋กœ ์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ๊ดœ์ฐฎ์ง€๋งŒ, ํ•ด๋‹น ์‹œ๊ฐ„์€ ์‹œ๊ฐ„๋Œ€๊ฐ€ ์ „๋‹ฌ๋  ๋•Œ ์ฃผ์–ด์ง„ ์‹œ๊ฐ„๋Œ€์— ๋Œ€ํ•ด ์ •ํ™•ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, 19:00 UTC๊ฐ€ 20:00 UTC๋กœ ์ „ํ™˜๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋…ผ์˜๋˜๋Š” ์˜ˆ์˜ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์˜ ํŠน์ • ์ง€์ .

์—ฌ๊ธฐ๋กœ ๋ฐฑ์—… - ๋ฌธ์„œํ™” ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

RRule _always_๋Š” JS "UTC" ๋‚ ์งœ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ ํ•ด๋‹น ๋‚ ์งœ๊ฐ€ UTC ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ์˜คํžˆ๋ ค, ์œ ์ผํ•œ ๋Œ€์•ˆ์ธ ํ˜„์ง€ ๋‚ ์งœ์˜ ์ œํ•œ์œผ๋กœ ์ธํ•ด ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ๋‚ ์งœ ๊ณ„์‚ฐ์— ์‹œ๊ฐ„๋Œ€ ์˜คํ”„์…‹์ด 0์ธ _only_ JS ๋‚ ์งœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒฐ์ •์ด ๋‚ด๋ ค์กŒ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ด๋Ÿฌํ•œ ์‹œ๊ฐ„์ด ์‹ค์ œ๋กœ UTC์˜ ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” TZID ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ‘œ์‹œ๋˜๋Š” ์‹œ๊ฐ„๋Œ€์—์„œ ํ•ด์„๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ์ž˜๋ชป๋œ ๋ถ€์ž‘์šฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Firefox์—์„œ ์˜๋„ํ•œ ๋™์ž‘์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ 19:00์„ ๋‚˜ํƒ€๋‚ด๋Š” Date๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ํ•ด๋‹น ์‹œ๊ฐ„์„ Denver ์‹œ๊ฐ„์œผ๋กœ ์š”์ฒญํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Denver ์‹œ๊ฐ„์— ๋งž๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. JavaScript์—์„œ ์œ ์šฉํ•œ ์œ ์ผํ•œ ์ข…๋ฅ˜์˜ ๋‚ ์งœ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋‚ ์งœ๊ฐ€ UTC ๋‚ ์งœ๋ผ๊ณ  ๋ณด๊ณ ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๋ฌด์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์‹  ์ด ๊ทœ์น™์— Denver ์‹œ๊ฐ„๋Œ€๊ฐ€ ์žˆ๋‹ค๋Š” ์ง€์‹์„ ์‚ฌ์šฉํ•˜์—ฌ 19:00์„ Denver์˜ ํ˜„์ง€ ์‹œ๊ฐ„์œผ๋กœ ํ•ด์„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋กœ์ปฌ ์‹œ์Šคํ…œ ์˜์—ญ ์™ธ๋ถ€์˜ ์‹œ๊ฐ„๋Œ€๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋” ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋Œ€์‹  America/New_York ์˜์—ญ ์ด๋ฆ„์œผ๋กœ RRule์„ ์„ค์ •ํ•˜๋ฉด ๋‚ ์งœ๊ฐ€ 17:00์œผ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ 19์ผ ๋•Œ ์ปดํ“จํ„ฐ์˜ ์‹œ๊ฐ„๋Œ€(Denver)์—์„œ ํ˜„์ง€ ์‹œ๊ฐ„์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. :00 ์š”์ฒญํ•œ ์‹œ๊ฐ„๋Œ€(๋‰ด์š•).

JavaScript๋Š” ๋‚ ์งœ๋ฅผ ํŠน์ • ์‹œ๊ฐ„๋Œ€์— "๊ณ ์ •"ํ•  ๋ฐฉ๋ฒ•์ด ์—†์œผ๋ฏ€๋กœ ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ ํ•ญ์ƒ ์ •ํ™•ํ•œ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋ฏ€๋กœ Date๊ฐ€ ์ž์ฒด์ ์œผ๋กœ UTC๋กœ ๋ณด๊ณ ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. RRule์—์„œ ์‹œ๊ฐ„๋Œ€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์‹ค์ œ๋กœ๋Š” ํ˜„์ง€ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด Firefox๊ฐ€ ์ •ํ™•ํ•œ ํ˜„์ง€ ์‹œ๊ฐ„์„ 19:00์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค(ํ•ด๋‹น ๋‚ ์งœ๊ฐ€ UTC๋กœ ๋ณด๊ณ ๋˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” Denver ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค).

์ด๊ฒƒ์ด ๋ชจ๋“  ๊ฒƒ์„ ๋ช…ํ™•ํžˆ ํ•ฉ๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ํ˜ผ๋ž€์Šค๋Ÿฝ๊ณ  ์ง๊ด€์ ์ด์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•˜์ง€๋งŒ, ์ผ๋‹จ ์ดํ•ด๋˜๋ฉด ์ถ”๋ก ํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ• ๋งŒํผ ๊ฐ„๋‹จํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ํ™•์‹คํžˆ ์ด๊ฒƒ์„ ๋” ์ž˜ ๋ฌธ์„œํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์—์„œ ๋‚ด๊ฐ€ ํƒ๊ตฌํ•˜๊ณ  ์‹ถ์€ ํ•œ ๊ฐ€์ง€๋Š” Luxon์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ ๋‚ ์งœ๋ฅผ ํ˜„์ง€ ์‹œ๊ฐ„๋Œ€๋กœ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ตœ์†Œํ•œ ๊ท€ํ•˜์˜ ๊ธฐ๋Œ€์™€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด ๋„ˆ๋ฌด ์–ด๋ ต์ง€ ์•Š์•„์•ผํ•˜๋ฉฐ ์ด์™€ ๊ฐ™์€ ์˜คํ•ด๋ฅผ ๋ช…ํ™•ํžˆํ•˜๋Š” ๋ฐ ๋„์›€์ด๋˜์–ด์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. Chrome ๋ฌธ์ œ๋Š” ๋‚˜์—๊ฒŒ ์ง„์ •์œผ๋กœ ์ƒˆ๋กœ์šด ๊ฒƒ์ด๋ฉฐ ๋‚˜์œ ์†Œ์‹์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด๋ฏ€๋กœ ๋ฐฐ์†กํ•˜๊ธฐ ์ „์— ์ปค๋ฎค๋‹ˆํ‹ฐ ํ”ผ๋“œ๋ฐฑ์„ ์œ„ํ•œ ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

@shorlbeck ์ด diff๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ˆ์ƒ๋˜๋Š” ๋™์ž‘์„ ๊ตฌํ˜„ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

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

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์ด ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ์™„์ „ํžˆ ํ™•์‹ ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ํด๋ผ์ด์–ธํŠธ์˜ ํ˜„์ง€ ์‹œ๊ฐ„๋Œ€์—์„œ ํ•ด์„๋˜๋„๋ก ์˜๋„๋œ "๋ถ€๋™" ์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ผ๋ฐ˜์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค(0 ์˜คํ”„์…‹์ด ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ ). rrule ์—์„œ ๋ฐ˜ํ™˜๋œ ๋‚ ์งœ์— ๋Œ€ํ•œ ์ ์ ˆํ•œ getter๋Š” getUTC* getter์ด๋ฏ€๋กœ ์˜ˆ๋ฅผ ๋“ค์–ด ์›๋ž˜ ๊ฒฐ๊ณผ์˜ ๊ฐ ๋‚ ์งœ์— getUTCHours() ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Chrome์—์„œ๋„ ์ •ํ™•ํ•œ ์‹œ๊ฐ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์˜ฌ๋ฐ”๋ฅธ ์ˆ˜์ •์€ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์˜์‚ฌ "UTC"(์‹ค์ œ๋กœ๋Š” "ํ”Œ๋กœํŒ…") ์‹œ๊ฐ„์˜ ๋‹ค์†Œ ํŠน์ดํ•œ ์‚ฌ์šฉ๋ฒ•์„ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฌธ์„œํ™”ํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ชฝ์œผ๋กœ ๊ธฐ์šธ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ด๊ฒƒ์— ๋Œ€ํ•ด ์„ค๋“๋ ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์„ค๋ช…๊ณผ ์ง€์นจ์œผ๋กœ README๋ฅผ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋…ผ์˜๋ฅผ ๊ณ„์†ํ•˜๋Š” ๋ฐ ๊ด€์‹ฌ์ด ์žˆ์œผ๋ฉฐ ํ–ฅํ›„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฒ„์ „์—์„œ ์ด ๋™์ž‘์„ ๋ณ€๊ฒฝํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ต๋ณ€๊ณผ ์ž์„ธํ•œ ์„ค๋ช… ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ถˆํ–‰ํžˆ๋„, ๋‚˜๋Š” ๊ฒฐ์ฝ” ๋” ํ˜ผ๋ž€์Šค๋Ÿฌ์›Œํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‚ ์งœ์™€ ์‹œ๊ฐ„์œผ๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์€ ๋‚ ์งœ ์‹œ๊ฐ„์ด ์‹ค์ œ๋กœ UTC์ด๊ฑฐ๋‚˜ ์‹ค์ œ๋กœ ํ˜„์ง€ ์‹œ๊ฐ„์ผ ๋•Œ ์ถฉ๋ถ„ํžˆ ์–ด๋ ต์ง€๋งŒ ๋•Œ๋•Œ๋กœ UTC ์‹œ๊ฐ„์ด ์‹ค์ œ๋กœ UTC ์‹œ๊ฐ„์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ๋‚ด ๋‘๋‡Œ๊ฐ€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ ์ด์ƒ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ์˜ค๋Š˜ ํ•˜๋ฃจ ์ข…์ผ ๋‹น์‹ ์ด ์“ด ๊ฒƒ์„ ์ดํ•ดํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์ง€๋งŒ ํ•˜๋ฃจ๊ฐ€ ๋๋‚˜๋ฉด ์™„์ „ํžˆ ๋ฐ˜์ง๊ด€์ ์ด์–ด์„œ ๋‚ด ๋งˆ์Œ์„ ๊ฐ์Œ€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ๋ฅผ ๋ณผ ๋•Œ๋งˆ๋‹ค "UTC" ๋‚ ์งœ๊ฐ€ ํ‘œ์‹œ๋  ๋•Œ๋งˆ๋‹ค ๊ทธ๊ฒƒ์ด ์‹ค์ œ๋กœ UTC ์ธ์ง€ ๊ทธ๋ƒฅ ์˜์‚ฌ UTC์ธ์ง€ ๊ธฐ์–ตํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๊ฑฐ๊ธฐ์— Chrome ๋ฌธ์ œ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‚ด๊ฐ€ ์ฝ”๋”ฉํ•˜๋Š” ๊ฒƒ์„ ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‚ด ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์— ๋Œ€ํ•œ ๋‚˜์˜ ์‹ ๋ขฐ๋ฅผ ๋ง์น˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ ๋‚ด ์งˆ๋ฌธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด... ์›๋ž˜์˜ ์˜ˆ๋กœ ๋Œ์•„๊ฐ€์„œ... ์‹ค์ œ UTC์—์„œ 2018-11-01 19:00:00 ๋Š” ๋ฏธ๊ตญ/๋ด๋ฒ„ ์‹œ๊ฐ„(13:00)์˜ ์˜คํ›„ 1์‹œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ œ ์˜ˆ์—์„œ๋Š” ์˜คํ›„ 1์‹œ America/Denver๊ฐ€ ์ œ ์ด๋ฒคํŠธ์˜ ์ •ํ™•ํ•œ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ UTC๊ฐ€ ์‹ค์ œ UTC๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ tzid ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ new Date(Date.UTC(2018, 10, 1, 13, 0, 0)) ๋˜๋Š” new Date(Date.UTC(2018, 10, 1, 19, 0, 0)) ๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? (์ฐจ์ด๋Š” 13:00 vs. 19:00)

์˜์‚ฌ UTC๊ฐ€ ๋“ค์–ด์˜ค๊ณ  ๋‚˜๊ฐ€๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์‹ค์ œ UTC๊ฐ€ ๋“ค์–ด๊ฐ€๊ณ  ์˜์‚ฌ UTC๊ฐ€ ๋‚˜์˜ค๋‚˜์š”? ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ป๊ฒŒ ์ด๊ฒƒ์„ ํ™•์‹คํ•˜๊ฒŒ ๊ธฐ์–ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ??

๊ทœ์น™์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง€์ •๋ฉ๋‹ˆ๋‹ค.

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

๊ทธ๋Ÿฌ๋‚˜ RRULE RFC5545์— ๋”ฐ๋ฅด๋ฉด ์ด๊ฒƒ์€ ํ˜„์ง€ ์‹œ๊ฐ„์ด 1900์ธ Denver ์‹œ๊ฐ„๋Œ€์˜ ๊ทœ์น™์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. 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

1300 ํ˜„์ง€ Denver ์‹œ๊ฐ„์— DTSTART๋ฅผ ์›ํ•˜๋ฉด ๊ทœ์น™์— 1300์œผ๋กœ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ด์™€ ๊ด€๋ จํ•˜์—ฌ ์‚ฌ์–‘์„ ์ฒ ์ €ํžˆ ์ค€์ˆ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (๊ธ€์Ž„, 1300 UTC๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ์‚ฌ์†Œํ•œ ์‚ฌ์‹ค์„ ์ œ์™ธํ•˜๊ณ  ...)

๋˜ํ•œ "๋ถ€๋™ ์‹œ๊ฐ„"์— ๋Œ€ํ•œ ์„น์…˜์„ ์ฝ์œผ์‹ญ์‹œ์˜ค.

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๋Š” ๋ถˆํ–‰ํžˆ๋„ "๋ถ€๋™ ์‹œ๊ฐ„"์˜ ๊ตฌํ˜„์„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. "์ˆœ์ˆ˜ํ•œ" ์‹œ๊ฐ„๋Œ€ ์—†๋Š” ์‹œ๊ฐ„์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์˜ต์…˜์€ UTC์ด๋ฏ€๋กœ "psuedo-UTC"์ž…๋‹ˆ๋‹ค. (์ฐธ๊ณ ๋กœ rrule ๋Š” TZID๋‚˜ Z UTC ์ง€์ •์ž๋กœ ์ง€์ •๋˜์ง€ ์•Š์€ ๋ถ€๋™ ์‹œ๊ฐ„๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.)

UTC๋กœ์˜ ์ „ํ™˜์€ ๋‹ค์Œ ์ปค๋ฐ‹์œผ๋กœ ์™„์ „ํžˆ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค. https://github.com/jakubroztocil/rrule/commit/850ed075175eb1acfcbd7b2cddf0606f2b2206f7

์ด์ „ ์ปค๋ฐ‹์„ ํ™•์ธํ•˜๊ณ  #850ed075์—์„œ ์ถ”๊ฐ€๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์„ ํƒํ•˜๋ฉด ์‹คํŒจํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค, ์ „์ฒด ์ œํ’ˆ๊ตฐ์ด UTC๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ํ•œ ์ˆ˜๋ฐฑ ๊ฐœ์˜ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. DST ๊ฒฝ๊ณ„์— ๊ฑธ์ณ ์žˆ๋Š” ๋ฏธ๋ž˜ ๋‚ ์งœ๋ฅผ ๊ณ„์‚ฐํ•˜๋ ค๋ฉด ํ˜„์žฌ ์‹œ๊ณ„๊ฐ€ DST์—์„œ ์‹คํ–‰ ์ค‘์ผ ๋•Œ ๋‚ ์งœ๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•œ ๋‹ค์Œ ํ‘œ์ค€ ์‹œ๊ฐ„์œผ๋กœ ์„ค์ •ํ•˜๋Š” ์ˆ˜ํ•™์„ ๊ณ„์‚ฐํ•ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž˜๋ชป๋œ 1์‹œ๊ฐ„ ์˜คํ”„์…‹์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ตฌํ˜„์€ ํ•ญ์ƒ ์ •ํ™•ํ•œ ํ˜„์ง€ ์‹œ๊ฐ„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋Š” ์‹ค์ œ๋กœ ๋ชจ๋“  ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ํ†ต๊ณผํ•  ๊ฒƒ์ด๋ฉฐ, ์ •ํ™•ํ•œ ๋‚ ์งœ/์‹œ๊ฐ„์„ ์–ป์œผ๋ ค๋ฉด getUTCDate() / getUTCHour() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ–ฅํ›„ ๋ฒ„์ „์—์„œ ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์กฐ์‚ฌํ•˜๊ณ  ์žˆ์ง€๋งŒ Luxon๊ณผ ๊ฐ™์€ ํ•„์ˆ˜ ํƒ€์‚ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฑ„ํƒํ•˜์ง€ ์•Š๊ณ ๋Š” ๋ณ€๊ฒฝ์ด ๊ฐ„๋‹จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ฒ„์ „์—์„œ๋Š” ํ˜„์ง€ ์‹œ๊ฐ„๋Œ€๋กœ ํ•ญ์ƒ ์ •ํ™•ํ•œ ๋‚ ์งœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋™์ž‘์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • DTSTART;TZID=America/Denver:20181101T130000 ๋Š” ๋ด๋ฒ„์— ์žˆ๋Š” ๊ฒฝ์šฐ 1300-0700 (ํ‘œ์ค€ ์‹œ๊ฐ„) ๋˜๋Š” UTC์— ์žˆ๋Š” ๊ฒฝ์šฐ 2000Z ์ธ ๋‚ ์งœ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • DTSTART=20181101T130000 ๋Š” ๋ด๋ฒ„์— ์žˆ์œผ๋ฉด 1300-0700 , UTC์— ์žˆ์œผ๋ฉด 1300Z ์ž…๋‹ˆ๋‹ค.
  • DTSTART=20181101T130000Z ๋Š” ๋ด๋ฒ„์— ์žˆ์œผ๋ฉด 0600-0700 , UTC์— ์žˆ์œผ๋ฉด 1300Z ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋‹น์‹ ์ด ๊ธฐ๋Œ€ํ•˜๋Š” ํ–‰๋™์ž…๋‹ˆ๊นŒ?

ํ•œ ๊ฐ€์ง€ ํฐ ๋ฌธ์ œ๋Š” ์ฃผ์–ด์ง„ ๋กœ์ปฌ JS ํ™˜๊ฒฝ์ด ๋‹จ์ผ ์‹œ๊ฐ„๋Œ€์˜ ๋‚ ์งœ๋งŒ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ new Date(...) ๋˜๋Š” new Date(Date.UTC(...)) ๋ชจ๋‘ ํŠน์ • ์‹œ๊ฐ„๋Œ€(ํ˜„์žฌ ์‹œ๊ฐ„๋Œ€์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ)์—์„œ ๋‚ ์งœ-์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋ ค๋Š” ๊ฒฝ์šฐ์—๋Š” ์ ์ ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ–‰๋™์„ ์ทจํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

๋กœ์ปฌ ๋จธ์‹ ์ด America/Los_Angeles ์— ์žˆ๋‹ค๋ฉด? ์ƒ์„ฑํ•˜๋Š” ์‹œ๊ฐ„๊ณผ ํ•ด๋‹น ์‹œ๊ฐ„์„ ์›ํ•˜๋Š” ์‹œ๊ฐ„๋Œ€ ์‚ฌ์ด์˜ 1์‹œ๊ฐ„ ์ฐจ์ด๋ฅผ ์ตœ์†Œํ•œ ์–ด๋Š ์ •๋„ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์˜ค์ „ 11์‹œ(Denver ์‹œ๊ฐ„, ์š”์ฒญํ•œ ์˜์—ญ) ๋˜๋Š” ์˜ค์ „ 10์‹œ( ๋กœ์Šค์•ค์ ค๋ ˆ์Šค ์‹œ๊ฐ„, ์ถœ๋ฐœ์ง€)? ๋‚˜๋Š” ๋‹น์‹ ์ด ๋‘˜ ์ค‘ ํ•˜๋‚˜์— ๋Œ€ํ•œ ์‚ฌ๋ก€๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

ํ˜„ํ•™์ ์œผ๋กœ Date ๊ฐ์ฒด๋Š” ์‹œ๊ฐ„๋Œ€ ๋˜๋Š” ์˜คํ”„์…‹ ๊ฐœ๋…์ด ์—†๋Š” UTC ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„์˜ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค๋Š” ์ ์—์„œ ISO ๋‚ ์งœ ๋ฌธ์ž์—ด ๋ฐ DTSTART ๋ฌธ์ž์—ด๊ณผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์›ํ•˜๋Š” ์‹œ๊ฐ„์˜ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ _์˜ฌ๋ฐ”๋ฅธ ์˜์—ญ์—์„œ_ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํ•ฉ๋ฆฌ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋กœ์Šค์•ค์ ค๋ ˆ์Šค์— ์žˆ๊ณ  ๋ด๋ฒ„์˜ ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋ ค๋ฉด ์ƒ๋Œ€ ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”๋œ Date ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜คํ”„์…‹์ด ์ ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค(๋”ฐ๋ผ์„œ ์œ„์˜ ์˜ˆ๋Š” ๋ด๋ฒ„์—์„œ ์˜ค์ „ 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. OS ์‹œ์Šคํ…œ ์‹œ๊ฐ„๋Œ€๋ฅผ 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. OS ์‹œ์Šคํ…œ ์‹œ๊ฐ„๋Œ€๋ฅผ 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)]

๊ทธ๋Ÿฌ๋‚˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‚ด ์‹œ์Šคํ…œ ์‹œ๊ฐ„๋Œ€๋‚˜ ์‹ค์ œ UTC๊ฐ€ ์•„๋‹Œ America/Denver ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. 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 ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์‹œ์Šคํ…œ ์‹œ๊ฐ„๋Œ€์— ๊ด€๊ณ„์—†์ด ํ•ญ์ƒ 13:00์„ ์ƒ์„ฑํ•˜๋ ค๋ฉด ๋ถ€๋™ ์‹œ๊ฐ„(๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ผํ•˜์ง€๋งŒ $#$ DSTART;TZID=<timezone>:<datetime> DSTART:<datetime> ์‚ฌ์šฉ)์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

13:00 ์œ ์‚ฌ UTC์ž…๋‹ˆ๋‹ค. Chrome ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์ด์ „ ๋Œ“๊ธ€์˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ์ปฌ ๋‚ ์งœ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@davidgoli ์˜ค, ๋‹น์‹  ๋ง์ด ๋งž์•„์š”. ๋ถ€๋™ ์‹œ๊ฐ„/์˜์‚ฌ UTC๊ฐ€ ์‹ค์ œ๋กœ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์„ ์ดํ•ดํ•œ ํ›„ ์‹ค์ œ๋กœ tzid ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์œ„์˜ .map() ์†”๋ฃจ์…˜๋„ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ์˜๊ฒฌ์€ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ๋‚˜๋ฅผ ์ง€์ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ •๋ง ๊ณ ๋งˆ์›Œ.

์ด๊ฒƒ์ด ๋‚ด๊ฐ€ ์˜ค๋žซ๋™์•ˆ ์ž‘์—…ํ•œ ๊ฒƒ ์ค‘ ๊ฐ€์žฅ ํ˜ผ๋ž€์Šค๋Ÿฌ์šด ์ผ์ด ์•„๋‹ˆ์—ˆ๋‹ค๋ฉด ๊ฑฐ์ง“๋ง์ด๊ฒ ์ง€๋งŒ, ์ง€๊ธˆ์€ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€๊ณ  ์žˆ์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค.

ํ•„์š”ํ•œ ๊ฒƒ์„ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ–ˆ์Šต๋‹ˆ๊นŒ(์‹œ๊ฐ„๋Œ€ ์—†๋Š” ๋ถ€๋™ ๋‚ ์งœ)? ๊ธฐ๋ณธ JS Date๋ฅผ UTC ์‹œ๊ฐ„๋Œ€ ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  ๋ฌธ์„œ์—์„œ UTC๋ผ๊ณ  ์ฃผ์žฅํ•˜๋Š” ๊ฒƒ์€ DST ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๊นŒ์ง€ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

(year, month, day, hours?, minutes?, seconds?, milliseconds?) ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  .toDate() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” "RRuleDate"๋ผ๊ณ  ํ•˜๋Š” ๊ฐœ์ฒด๋ฅผ ์ œ์•ˆํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด RRuleDate๋Š” before() , after() , between() ๋ฐ all() ๊ฐ€ ๋ฐ˜ํ™˜๋˜์–ด์•ผ ํ•˜๋Š” ํ•ญ๋ชฉ์ด๋ฉฐ before() , after() , between() ๋ฐ dtstart ์˜ต์…˜์€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ—ˆ์šฉ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰