Rrule: UTCμ΄μ§€λ§Œ UTCκ°€ μ•„λ‹Œ ν˜Όλ™

에 λ§Œλ“  2019λ…„ 04μ›” 05일  Β·  18μ½”λ©˜νŠΈ  Β·  좜처: jakubroztocil/rrule

λ‚˜λŠ” 이것이 rrule의 λ¬Έμ œκ°€ μ•„λ‹ˆλΌ 그것에 λŒ€ν•œ λ‚˜μ˜ 이해 및/λ˜λŠ” μ‹œκ°„λŒ€ 지원에 λ¬Έμ œκ°€ μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€. λ‚˜λŠ” 이미 rrule 및 luxon λ¬Έμ„œμ—μ„œ κ΄€λ ¨ μ„Ήμ…˜μ„ μ½μ—ˆμŠ΅λ‹ˆλ‹€.

μ½”λ“œ μƒ˜ν”Œ

const { RRule, RRuleSet } = require(`rrule`);

const dtstart = new Date();
console.log(`dtstart`, dtstart);

const firstHour = dtstart.getHours();
const secondHour = (firstHour + 2) % 24;

const getRRule = options => new RRule(Object.assign({}, options, {
    freq: RRule.DAILY,
    count: 1,
    dtstart,
    byminute: 0,
    bysecond: 0
}));

const showNextInstance = tzid => {
    const ruleSet = new RRuleSet();
    ruleSet.rrule(getRRule({ tzid, byhour: firstHour }));
    ruleSet.rrule(getRRule({ tzid, byhour: secondHour }));
    console.log(tzid, ruleSet.after(dtstart));
};

showNextInstance(undefined);
showNextInstance(`UTC`);
showNextInstance(`local`);
showNextInstance(`America/Chicago`);

μ‹€μ œ 좜λ ₯

dtstart 2019-04-05T11:51:23.744Z
undefined 2019-04-06T06:00:00.744Z
UTC 2019-04-06T06:00:00.744Z
local 2019-04-06T06:00:00.744Z
America/Chicago 2019-04-06T06:00:00.744Z

μ˜ˆμƒ 좜λ ₯

tzid 의 사양이 좜λ ₯에 μ•½κ°„μ˜ 영ν–₯을 쀄 것이라고 μƒκ°ν•˜μ§€λ§Œ 각 μ‹œκ°„λŒ€μ— λŒ€ν•΄ λ™μΌν•œ 좜λ ₯이 μƒμ„±λ©λ‹ˆλ‹€.

λ˜ν•œ κ·œμΉ™μ— 제곡된 μ‹œκ°„κ³Ό λ‹€μŒ 이벀트 μΈμŠ€ν„΄μŠ€μ˜ 좜λ ₯ 사이에 상관 관계가 무엇인지 μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€. λ‹€μŒ μΈμŠ€ν„΄μŠ€λŠ” μ§€μ •λœ dtstart 및 tzid 값이 ν•΄μ„λ˜κ±°λ‚˜ μ μš©λ˜λŠ” 방식에 따라 제곡된 κ·œμΉ™μ΄ λ‚˜νƒ€λ‚΄λŠ” 2μ‹œκ°„ 쀑 ν•˜λ‚˜μ— λŒ€λž΅ ν•΄λ‹Ήν•  κ²ƒμœΌλ‘œ μ˜ˆμƒν•©λ‹ˆλ‹€.

κ·œμΉ™ 버전

2.6.0

운영 체제

OS X 10.14.2, λ…Έλ“œ 8.11.4

ν˜„μ§€ μ‹œκ°„λŒ€

$ date
Fri Apr  5 06:32:33 CDT 2019

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

Date 개체의 λ¬Έμ œμ™€ RRULE λŒ€ν•œ λ°œμƒ 계산 문제λ₯Ό λ³‘ν•©ν•˜λŠ” 것이 쉽닀고 μƒκ°ν•˜λ―€λ‘œ 이 λ‘˜μ„ κ²°ν•©ν•˜κΈ° 전에 λ³„λ„λ‘œ μ΄μ•ΌκΈ°ν•˜κ² μŠ΅λ‹ˆλ‹€. λ‚˜λŠ” λ¬Έμ œμ— λŒ€ν•œ λ‚˜μ˜ 이해λ₯Ό μ •λ¦¬ν•˜κ³  λ‹€λ₯Έ μ‚¬λžŒλ“€μ—κ²Œ 도움이 λ˜λŠ” 경우λ₯Ό λŒ€λΉ„ν•˜μ—¬ 여기에 κ²Œμ‹œν•  생각을 ν•˜κΈ° μœ„ν•΄ 이것을 주둜 μž‘μ„±ν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈ λ‚ μ§œ 객체

λ‚˜λŠ” JS Date 객체가 λΆˆκ·œμΉ™ν•˜λ‹€λŠ” 데 λ™μ˜ν•˜μ§€ μ•Šμ§€λ§Œ κ·Έλ ‡κ²Œ 보이게 λ§Œλ“œλŠ” 객체에 λŒ€ν•œ 일반적인 μ˜€ν•΄κ°€ μžˆμŠ΅λ‹ˆλ‹€. ν•œλ§ˆλ””λ‘œ ν˜Όλž€μŠ€λŸ½μŠ΅λ‹ˆλ‹€.

@hlee5zebraκ°€ μ–ΈκΈ‰ν–ˆλ“―μ΄ Date μƒμ„±μžλ‘œ μƒμ„±λœ λ‚ μ§œλŠ” 항상 UTC둜 ν‘œμ‹œλ©λ‹ˆλ‹€. ν˜„μ§€ μ‹œκ°„μœΌλ‘œ ν‘œμ‹œλ  수 μžˆμ§€λ§Œ μ‹€μ œλ‘œλŠ” Unix Epoch μ‹œκ°„μœΌλ‘œ ν‘œμ‹œλ©λ‹ˆλ‹€. μΈμŠ€ν„΄μŠ€ν™”λœ Date κ°œμ²΄μ—μ„œ 항상 .getTime() λ˜λŠ” .toISOString() λ₯Ό μ‹€ν–‰ν•˜μ—¬ UTC둜 μž‘μ—…ν•˜κ³  μžˆμŒμ„ μŠ€μŠ€λ‘œμ—κ²Œ μƒκΈ°μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

κ΅¬ν˜„μ€ λ˜ν•œ λͺ¨λ“  처리λ₯Ό UTC둜 μˆ˜ν–‰ν•˜κ³  μ‚¬μš©μžμ—κ²Œ ν‘œμ‹œλ˜λŠ” ν˜„μ§€ μ‹œκ°„μœΌλ‘œλ§Œ λ³€ν™˜ν•˜λŠ” κ·œμΉ™μ„ μ˜¬λ°”λ₯΄κ²Œ λ”°λ¦…λ‹ˆλ‹€. 개발자인 μš°λ¦¬λŠ” μ‚¬μš©μžμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μš°λ¦¬λŠ” 물을 흐리게 ν•˜λŠ” 우리 μžμ‹ μ˜ μ‚¬μš©μžλ₯Ό 염두에 두고 μžˆμŠ΅λ‹ˆλ‹€. 앱을 κ°œλ°œν•  λ•Œ κ·œμΉ™μ„ 따라야 ν•˜κ³  λ°±μ—”λ“œμ—μ„œ λ‚ μ§œλ₯Ό UTC둜 μ €μž₯ν•΄μ•Ό ν•©λ‹ˆλ‹€(JSλŠ” 이미 UTC둜 λ‚ μ§œλ₯Ό μ²˜λ¦¬ν•˜λ―€λ‘œ 잘 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€). 데이터 μ €μž₯μ†Œμ— UTC둜 λ‚ μ§œλ₯Ό μ €μž₯ν•˜λ©΄ ISO λ¬Έμžμ—΄ 둜 ν΄λΌμ΄μ–ΈνŠΈμ— λ„μ°©ν•˜κ³  Date κ°œμ²΄μ— μ˜ν•΄ μ˜¬λ°”λ₯΄κ²Œ ꡬ문 λΆ„μ„λ˜κ³  μ‚¬μš©μžμ˜ ν˜„μ§€ μ‹œκ°„μœΌλ‘œ ν‘œμ‹œλ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

이 글을 톡해 Date 개체λ₯Ό μ΄ν•΄ν•˜κ³  개체둜 μ–Όλ§ˆλ‚˜ λ§Žμ€ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλŠ”μ§€ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. https://www.toptal.com/software/definitive-guide-to-datetime-manipulation

κ·œμΉ™

RRULE λŒ€ν•œ λ°œμƒ 계산은 μ‚¬μš©μž κΈ°λŒ€μΉ˜μ™€ UTC둜 λ‚ μ§œ μ²˜λ¦¬μ— 근본적인 차이가 있기 λ•Œλ¬Έμ— κΉŒλ‹€λ‘œμ›Œμ§‘λ‹ˆλ‹€. 이 두 μ‹œκ°„ ν‘œν˜„μ΄ ν•˜λ£¨ μž₯벽을 λ„˜μ„ λ•Œ λˆˆμ— λ„κ²Œ λ©λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄, μ‚¬μš©μžκ°€ 맀주 ν™”μš”μΌμ— λ°œμƒν•˜λŠ” λ°˜λ³΅μ„ μ›ν•˜λ©΄ λͺ¨λ“  λ°œμƒμ΄ ν˜„μ§€ μ‹œκ°„μœΌλ‘œ ν™”μš”μΌμ— μžˆμ„ κ²ƒμœΌλ‘œ μ˜ˆμƒν•  κ²ƒμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ λͺ¨λ“  계산은 UTC둜 μˆ˜ν–‰λ˜λ―€λ‘œ RRULE λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

DTSTART:2019-10-29T00:02:26Z
RRULE:FREQ=WEEKLY;BYWEEKDAY=TU

UTC둜 ν™”μš”μΌμ— λ°œμƒμ„ λ°˜ν™˜ν•˜μ§€λ§Œ 본초 μžμ˜€μ„ μ—μ„œ μƒλ‹Ήνžˆ μ„œμͺ½μ— μžˆλŠ” μ‚¬μš©μžμ˜ 경우 λ°œμƒμ€ ν˜„μ§€ μ‹œκ°„μœΌλ‘œ μ›”μš”μΌμ— ν‘œμ‹œλ˜λ©° μ΄λŠ” λΆ„λͺ…νžˆ λˆ„κ΅¬λ„ μ›ν•˜λŠ” κ²°κ³Όκ°€ μ•„λ‹™λ‹ˆλ‹€.

λ°”λ‘œμž‘κΈ°

즉, κ°œλ°œμžκ°€ 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” 일은 JavaScriptκ°€ 둜컬 λ‚ μ§œκ°€ μ‹€μ œλ‘œ UTC λ‚ μ§œλΌκ³  μƒκ°ν•˜λ„λ‘ κ°•μ œν•˜κ³  ν•΄λ‹Ή "κ°€μ§œ" λ‚ μ§œλ₯Ό rrule.js 에 μ§€μ •ν•˜κ³  λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ 계산을 μˆ˜ν–‰ν•˜λ„λ‘ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 이 ν˜„μ§€ UTC μ‹œκ°„. .all() λ©”μ„œλ“œμ—μ„œ λ°œμƒ ν•­λͺ©μ„ κ°€μ Έμ˜€λ©΄ ν•΄λ‹Ή λ‚ μ§œμ˜ "UTC" 뢀뢄을 μ‚¬μš©ν•˜μ—¬ μƒˆ Date 개체λ₯Ό μΈμŠ€ν„΄μŠ€ν™”ν•©λ‹ˆλ‹€. μ΄λŠ” κ²°κ³Όκ°€ μ˜ˆμƒκ³Ό 효과적으둜 μΌμΉ˜ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.

import { RRule } from 'rrule'

const dtstart = new Date('2019-10-29T00:02:26Z') // same as: `new Date(Date.UTC(2019, 9, 29, 0, 2, 26))`
console.log(dtstart.toISOString()) // 2019-10-29T00:02:26.000Z
console.log(dtstart) // Mon Oct 28 2019 17:02:26 GMT-0700 (Pacific Daylight Time)

const fakeDateStart = setPartsToUTCDate(dtstart)
console.log(fakeDateStart.toISOString()) // 2019-10-28T17:02:26.000Z
console.log(fakeDateStart) // Mon Oct 28 2019 10:02:26 GMT-0700 (Pacific Daylight Time)

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

const localUTCOccurrences = rule.all()
console.log(localUTCOccurrences.map(toISOString)) // ["2019-10-29T17:02:26.000Z", "2019-11-05T17:02:26.000Z"]
console.log(localUTCOccurrences) // [Tue Oct 29 2019 10:02:26 GMT-0700 (Pacific Daylight Time), Tue Nov 05 2019 09:02:26 GMT-0800 (Pacific Standard Time)]

const occurrences = localUTCOccurrences.map(setUTCPartsToDate)
console.log(occurrences.map(toISOString)) // ["2019-10-30T00:02:26.000Z", "2019-11-06T01:02:26.000Z"]
console.log(occurrences) // [Tue Oct 29 2019 17:02:26 GMT-0700 (Pacific Daylight Time), Tue Nov 05 2019 17:02:26 GMT-0800 (Pacific Standard Time)]

function setPartsToUTCDate(d) {
  return new Date(
    Date.UTC(
      d.getFullYear(),
      d.getMonth(),
      d.getDate(),
      d.getHours(),
      d.getMinutes(),
      d.getSeconds()
    )
  )
}

function setUTCPartsToDate(d) {
  return new Date(
    d.getUTCFullYear(),
    d.getUTCMonth(),
    d.getUTCDate(),
    d.getUTCHours(),
    d.getUTCMinutes(),
    d.getUTCSeconds()
  )
}

function toISOString(d) {
  return d.toISOString()
}

https://codesandbox.io/s/rrule-localutc-conversion-zxlki

λ˜λŠ” κ°„λ‹¨νžˆ λ§ν•΄μ„œ:

import { RRule } from 'rrule'

const date = new Date('2019-10-29T00:02:26Z')

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

const occurrences = rule.all().map(setUTCPartsToDate)

https://codesandbox.io/s/rrule-localutc-conversion-in-short-ez7g0

λ‚΄κ°€ λ³Ό 수 μžˆλŠ” ν•œ Chrome, Firefox 및 SafariλŠ” λͺ¨λ‘ μ½˜μ†”μ— λ‘œκ·ΈμΈν•  λ•Œ ν˜„μ§€ μ‹œκ°„μ˜ λ¬Έμžμ—΄λ‘œ λ‚ μ§œλ₯Ό ν‘œμ‹œν•˜κ³  Node.jsλŠ” UTC의 ISO λ¬Έμžμ—΄λ‘œ ν‘œμ‹œν•©λ‹ˆλ‹€.

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

저도 많이 ν—·κ°ˆλ¦½λ‹ˆλ‹€.

μ•Œκ² μŠ΅λ‹ˆλ‹€. 읽어보기에 λ‹€μŒκ³Ό 같은 핡심 단락이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

image

ν˜„μ§€ μ‹œκ°„λŒ€μ— 이 두 가지 λ„μš°λ―Έ κΈ°λŠ₯을 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. tzidλ₯Ό μ§€μ›ν•˜λ„λ‘ μ˜€ν”„μ…‹μ„ μˆ˜μ •ν•˜λ €λ©΄ X μ‹œκ°„λŒ€μ— λŒ€ν•œ μ˜€ν”„μ…‹μ„ κ°€μ Έμ˜€λ„λ‘ μ˜€ν”„μ…‹μ„ μˆ˜μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

/**
 * Mutates date. Add timezone to the date.
 *
 * <strong i="9">@param</strong> {Date} [date]
 */
export function localizeDate(date = new Date()) {
  const offset = new Date().getTimezoneOffset() * 60 * 1000; // get offset for local timezone (can modify here to support tzid)
  date.setTime(date.getTime() - offset);
  return date;
}

/**
 * Mutates a date. De-timezones a date.
 *
 * <strong i="10">@param</strong> {Date} [date]
 */
export function utcifyDate(date = new Date()) {
  const offset = new Date().getTimezoneOffset() * 60 * 1000; // get offset for local timezone (can modify here to support tzid)
  date.setTime(date.getTime() + offset);
  return date;
}

그러면 μ½”λ“œκ°€ λ‹€μŒκ³Ό 같이 λ³€κ²½λ©λ‹ˆλ‹€.

const { RRule, RRuleSet } = require(`rrule`);

const dtstart = localizeDate(new Date());
console.log(`dtstart`, utcifyDate(new Date(dtstart))); // even though it is utc string, this is the local time

const firstHour = dtstart.getUTCHours();
const secondHour = (firstHour + 2) % 24;

const getRRule = options => new RRule(Object.assign({}, options, {
    freq: RRule.DAILY,
    count: 1,
    dtstart,
    byminute: 0,
    bysecond: 0
}));

const showNextInstance = tzid => {
    const ruleSet = new RRuleSet();
    ruleSet.rrule(getRRule({ byhour: firstHour })); // remove tzid as i dont support this
    ruleSet.rrule(getRRule({ byhour: secondHour })); // removed tzid as i dont support this
    console.log('local:', utcifyDate(ruleSet.after(dtstart));
};

showNextInstance(undefined);
showNextInstance(`UTC`);
showNextInstance(`local`);
showNextInstance(`America/Chicago`);

JS의 ν˜Όλž€μŠ€λŸ½κ³  λΆˆκ·œμΉ™ν•œ Date κ΅¬ν˜„μ— 따라 κ΅¬μΆ•λœ 이 λΌμ΄λΈŒλŸ¬λ¦¬μ—λŠ” 이 문제λ₯Ό μš°νšŒν•˜λŠ” 쒋은 방법이 없을 것 κ°™μŠ΅λ‹ˆλ‹€. ν˜„μž¬ μ™„μ „ν•œ μž¬μž‘μ„± μž‘μ—…μ„ ν•˜κ³  μžˆμ§€λ§Œ μ œλŒ€λ‘œ ν•˜λ €λ©΄ μ‹œκ°„μ΄ μ’€ 걸릴 κ²ƒμž…λ‹ˆλ‹€.

κ°μ‚¬ν•©λ‹ˆλ‹€ @davidgoli λ‹Ήμ‹ μ˜ μž‘μ—…μ€ 정말 κ°μ‚¬ν•©λ‹ˆλ‹€!!!

λ‹€λ₯Έ μ‚¬λžŒλ“€μ΄ λΆˆκ·œμΉ™ν•œ λ‚ μ§œ κ΅¬ν˜„μ„ μ΄ν•΄ν•˜λŠ” 데 도움이 λ˜λŠ” 기사λ₯Ό μž‘μ„±ν•˜λ €λ©΄ μ΄ν•΄ν•˜λŠ” μ‚¬λžŒμ΄ ν•„μš”ν•˜λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€. μ†”μ§νžˆ 아직도 이해가 μ•ˆ κ°€λŠ”λ°, νš¨κ³Όκ°€ μžˆμ„ λ•ŒκΉŒμ§€ μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.

μ½˜μ†”μ— μΈμ‡„ν•˜λŠ” λ°˜ν™˜λœ λ‚ μ§œλŠ” λ¬Έμžμ—΄λ‘œ λ³€ν™˜λ˜λŠ” JavaScript λ‚ μ§œμ΄κ³  JavaScriptλŠ” UTC둜 λ‚ μ§œλ₯Ό μ½˜μ†”μ— μžλ™μœΌλ‘œ μΈμ‡„ν•˜κΈ° λ•Œλ¬ΈμΌ 수 μžˆμŠ΅λ‹ˆλ‹€(νƒ€μž„μŠ€νƒ¬ν”„ 끝에 μžˆλŠ” 'Z' μ°Έμ‘° 제둜 μ˜€ν”„μ…‹). λ„€ 가지 경우 각각의 λ‹€μŒ λ°œμƒμ΄ μ •ν™•νžˆ 같은 μ‹œκ°„μ— λ°œμƒν•˜κΈ° λ•Œλ¬Έμ—(λ‹€λ₯Έ μ‹œκ°„λŒ€μ— μžˆμŒμ—λ„ λΆˆκ΅¬ν•˜κ³ ) μ •ν™•νžˆ λ™μΌν•œ νƒ€μž„μŠ€νƒ¬ν”„λ₯Ό λ„€ 번 μΈμ‡„ν•©λ‹ˆλ‹€.

λ˜ν•œ JavaScript Date κ°μ²΄λŠ” 1970λ…„ 1μ›” 1일 λͺ©μš”일 00:00:00 UTC 이후 경과된 λ°€λ¦¬μ΄ˆ 수λ₯Ό μ‚¬μš©ν•˜μ—¬ μ‹œκ°„μ„ μΆ”μ ν•©λ‹ˆλ‹€. "12:34:56pm"에 λŒ€ν•œ μΌμ’…μ˜ λ¬Έμžμ—΄ 해석 λ˜λŠ” 이와 μœ μ‚¬ν•œ κ²ƒμž…λ‹ˆλ‹€.

@davidgoli

ν˜„μž¬ μ™„μ „ν•œ μž¬μž‘μ„± μž‘μ—…μ„ ν•˜κ³  μžˆμ§€λ§Œ μ œλŒ€λ‘œ ν•˜λ €λ©΄ μ‹œκ°„μ΄ μ’€ 걸릴 κ²ƒμž…λ‹ˆλ‹€.

rSchedule의 μ†ŒμŠ€ λ₯Ό ν™•μΈν•˜κ³  싢을 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. λͺ¨λ“  λ°˜λ³΅μ€ μž…λ ₯을 λ°›μ•„ μž…λ ₯κ³Ό λ™μΌν•˜κ²Œ λ³΄μ΄λŠ” 뢀동 utc λ‚ μ§œλ‘œ λ³€ν™˜ν•˜λŠ” 특수 DateTime 개체 (Luxon이 μ•„λ‹˜)둜 μˆ˜ν–‰λ©λ‹ˆλ‹€. 이 UTC datetime으둜 반볡이 μˆ˜ν–‰λ˜λ―€λ‘œ DSTλŠ” λ¬Έμ œκ°€ λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ°œμƒμ΄ 발견된 ν›„ μ‚¬μš©μžμ—κ²Œ 제곡되기 전에 ν•΄λ‹Ή μ‹œκ°„λŒ€μ˜ 정상 λ‚ μ§œλ‘œ λ‹€μ‹œ λ³€ν™˜λ©λ‹ˆλ‹€.

예: 이 λ‚ μ§œ

import { DateTime as LuxonDateTime } from 'luxon';

const date = LuxonDateTime.fromObject({
  year: 2019,
  month: 1,
  day: 1,
  zone: 'America/Los_Angeles'
})

이 UTC λ‚ μ§œλ‘œ λ³€ν™˜λ˜κ³  μ‹œκ°„λŒ€κ°€ μ €μž₯λ©λ‹ˆλ‹€.

const fakeDate = Date.UTC(2019, 0, 1)
const fakeDateZone = 'America/Los_Angeles'

이 UTC λ‚ μ§œλŠ” Luxon λ‚ μ§œμ™€ μœ μ‚¬ν•˜κ²Œ λ³΄μ΄μ§€λ§Œ(λ‘˜ λ‹€ 2019/1/1 처럼 λ³΄μž…λ‹ˆλ‹€) λ‹€λ₯Έ μ‹œκ°„λŒ€μ— 있기 λ•Œλ¬Έμ— μ‹€μ œλ‘œ 같은 μ‹œκ°„μ„ λ‚˜νƒ€λ‚΄μ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€.

즉, 이 νŠΉλ³„ν•œ "UTC" λ‚ μ§œλ‘œ λ°˜λ³΅ν•˜λ©΄ 일광 μ ˆμ•½ μ‹œκ°„μ— λŒ€ν•΄ κ±±μ •ν•˜μ§€ μ•Šκ³  λ°˜λ³΅ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ •ν™•ν•œ λ‚ μ§œλ₯Ό 찾으면 luxon λ‚ μ§œλ‘œ λ‹€μ‹œ λ³€ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄:

LuxonDateTime.fromObject({
  year: fakeDate.year,
  month: fakeDate.month,
  day: fakeDate.day,
  zone: fakeDateZone,
})

이것은 μž„μ˜μ˜ μ‹œκ°„λŒ€λ₯Ό 계속 μ§€μ›ν•˜λ©΄μ„œ 반볡 λ‘œμ§μ„ UTC에 μΉœμˆ™ν•˜κ²Œ μœ μ§€ν•©λ‹ˆλ‹€.

@fliik λ‚˜λŠ” ν”„λ¦¬λ―Έν‹°λΈŒλ₯Ό λ‹€μš΄; μ—¬κΈ°μ—μ„œ μ§„μ§œ 골칫거리가 λ˜λŠ” 것은 λͺ‡ μ£Όμž…λ‹ˆλ‹€. rschedule도 byweekno 및 bysetpos 지원에 λŒ€ν•΄ (μ§€κΈˆκΉŒμ§€) μ–ΈκΈ‰ν–ˆμ§€λ§Œ ...

μ•„ 그래. byweekno 은(λŠ”) 맀우 μ§œμ¦λ‚©λ‹ˆλ‹€.

μ–΄λ–€ λ‰΄μŠ€?

Date 개체의 λ¬Έμ œμ™€ RRULE λŒ€ν•œ λ°œμƒ 계산 문제λ₯Ό λ³‘ν•©ν•˜λŠ” 것이 쉽닀고 μƒκ°ν•˜λ―€λ‘œ 이 λ‘˜μ„ κ²°ν•©ν•˜κΈ° 전에 λ³„λ„λ‘œ μ΄μ•ΌκΈ°ν•˜κ² μŠ΅λ‹ˆλ‹€. λ‚˜λŠ” λ¬Έμ œμ— λŒ€ν•œ λ‚˜μ˜ 이해λ₯Ό μ •λ¦¬ν•˜κ³  λ‹€λ₯Έ μ‚¬λžŒλ“€μ—κ²Œ 도움이 λ˜λŠ” 경우λ₯Ό λŒ€λΉ„ν•˜μ—¬ 여기에 κ²Œμ‹œν•  생각을 ν•˜κΈ° μœ„ν•΄ 이것을 주둜 μž‘μ„±ν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈ λ‚ μ§œ 객체

λ‚˜λŠ” JS Date 객체가 λΆˆκ·œμΉ™ν•˜λ‹€λŠ” 데 λ™μ˜ν•˜μ§€ μ•Šμ§€λ§Œ κ·Έλ ‡κ²Œ 보이게 λ§Œλ“œλŠ” 객체에 λŒ€ν•œ 일반적인 μ˜€ν•΄κ°€ μžˆμŠ΅λ‹ˆλ‹€. ν•œλ§ˆλ””λ‘œ ν˜Όλž€μŠ€λŸ½μŠ΅λ‹ˆλ‹€.

@hlee5zebraκ°€ μ–ΈκΈ‰ν–ˆλ“―μ΄ Date μƒμ„±μžλ‘œ μƒμ„±λœ λ‚ μ§œλŠ” 항상 UTC둜 ν‘œμ‹œλ©λ‹ˆλ‹€. ν˜„μ§€ μ‹œκ°„μœΌλ‘œ ν‘œμ‹œλ  수 μžˆμ§€λ§Œ μ‹€μ œλ‘œλŠ” Unix Epoch μ‹œκ°„μœΌλ‘œ ν‘œμ‹œλ©λ‹ˆλ‹€. μΈμŠ€ν„΄μŠ€ν™”λœ Date κ°œμ²΄μ—μ„œ 항상 .getTime() λ˜λŠ” .toISOString() λ₯Ό μ‹€ν–‰ν•˜μ—¬ UTC둜 μž‘μ—…ν•˜κ³  μžˆμŒμ„ μŠ€μŠ€λ‘œμ—κ²Œ μƒκΈ°μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

κ΅¬ν˜„μ€ λ˜ν•œ λͺ¨λ“  처리λ₯Ό UTC둜 μˆ˜ν–‰ν•˜κ³  μ‚¬μš©μžμ—κ²Œ ν‘œμ‹œλ˜λŠ” ν˜„μ§€ μ‹œκ°„μœΌλ‘œλ§Œ λ³€ν™˜ν•˜λŠ” κ·œμΉ™μ„ μ˜¬λ°”λ₯΄κ²Œ λ”°λ¦…λ‹ˆλ‹€. 개발자인 μš°λ¦¬λŠ” μ‚¬μš©μžμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μš°λ¦¬λŠ” 물을 흐리게 ν•˜λŠ” 우리 μžμ‹ μ˜ μ‚¬μš©μžλ₯Ό 염두에 두고 μžˆμŠ΅λ‹ˆλ‹€. 앱을 κ°œλ°œν•  λ•Œ κ·œμΉ™μ„ 따라야 ν•˜κ³  λ°±μ—”λ“œμ—μ„œ λ‚ μ§œλ₯Ό UTC둜 μ €μž₯ν•΄μ•Ό ν•©λ‹ˆλ‹€(JSλŠ” 이미 UTC둜 λ‚ μ§œλ₯Ό μ²˜λ¦¬ν•˜λ―€λ‘œ 잘 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€). 데이터 μ €μž₯μ†Œμ— UTC둜 λ‚ μ§œλ₯Ό μ €μž₯ν•˜λ©΄ ISO λ¬Έμžμ—΄ 둜 ν΄λΌμ΄μ–ΈνŠΈμ— λ„μ°©ν•˜κ³  Date κ°œμ²΄μ— μ˜ν•΄ μ˜¬λ°”λ₯΄κ²Œ ꡬ문 λΆ„μ„λ˜κ³  μ‚¬μš©μžμ˜ ν˜„μ§€ μ‹œκ°„μœΌλ‘œ ν‘œμ‹œλ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

이 글을 톡해 Date 개체λ₯Ό μ΄ν•΄ν•˜κ³  개체둜 μ–Όλ§ˆλ‚˜ λ§Žμ€ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλŠ”μ§€ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. https://www.toptal.com/software/definitive-guide-to-datetime-manipulation

κ·œμΉ™

RRULE λŒ€ν•œ λ°œμƒ 계산은 μ‚¬μš©μž κΈ°λŒ€μΉ˜μ™€ UTC둜 λ‚ μ§œ μ²˜λ¦¬μ— 근본적인 차이가 있기 λ•Œλ¬Έμ— κΉŒλ‹€λ‘œμ›Œμ§‘λ‹ˆλ‹€. 이 두 μ‹œκ°„ ν‘œν˜„μ΄ ν•˜λ£¨ μž₯벽을 λ„˜μ„ λ•Œ λˆˆμ— λ„κ²Œ λ©λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄, μ‚¬μš©μžκ°€ 맀주 ν™”μš”μΌμ— λ°œμƒν•˜λŠ” λ°˜λ³΅μ„ μ›ν•˜λ©΄ λͺ¨λ“  λ°œμƒμ΄ ν˜„μ§€ μ‹œκ°„μœΌλ‘œ ν™”μš”μΌμ— μžˆμ„ κ²ƒμœΌλ‘œ μ˜ˆμƒν•  κ²ƒμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ λͺ¨λ“  계산은 UTC둜 μˆ˜ν–‰λ˜λ―€λ‘œ RRULE λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

DTSTART:2019-10-29T00:02:26Z
RRULE:FREQ=WEEKLY;BYWEEKDAY=TU

UTC둜 ν™”μš”μΌμ— λ°œμƒμ„ λ°˜ν™˜ν•˜μ§€λ§Œ 본초 μžμ˜€μ„ μ—μ„œ μƒλ‹Ήνžˆ μ„œμͺ½μ— μžˆλŠ” μ‚¬μš©μžμ˜ 경우 λ°œμƒμ€ ν˜„μ§€ μ‹œκ°„μœΌλ‘œ μ›”μš”μΌμ— ν‘œμ‹œλ˜λ©° μ΄λŠ” λΆ„λͺ…νžˆ λˆ„κ΅¬λ„ μ›ν•˜λŠ” κ²°κ³Όκ°€ μ•„λ‹™λ‹ˆλ‹€.

λ°”λ‘œμž‘κΈ°

즉, κ°œλ°œμžκ°€ 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” 일은 JavaScriptκ°€ 둜컬 λ‚ μ§œκ°€ μ‹€μ œλ‘œ UTC λ‚ μ§œλΌκ³  μƒκ°ν•˜λ„λ‘ κ°•μ œν•˜κ³  ν•΄λ‹Ή "κ°€μ§œ" λ‚ μ§œλ₯Ό rrule.js 에 μ§€μ •ν•˜κ³  λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ 계산을 μˆ˜ν–‰ν•˜λ„λ‘ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 이 ν˜„μ§€ UTC μ‹œκ°„. .all() λ©”μ„œλ“œμ—μ„œ λ°œμƒ ν•­λͺ©μ„ κ°€μ Έμ˜€λ©΄ ν•΄λ‹Ή λ‚ μ§œμ˜ "UTC" 뢀뢄을 μ‚¬μš©ν•˜μ—¬ μƒˆ Date 개체λ₯Ό μΈμŠ€ν„΄μŠ€ν™”ν•©λ‹ˆλ‹€. μ΄λŠ” κ²°κ³Όκ°€ μ˜ˆμƒκ³Ό 효과적으둜 μΌμΉ˜ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.

import { RRule } from 'rrule'

const dtstart = new Date('2019-10-29T00:02:26Z') // same as: `new Date(Date.UTC(2019, 9, 29, 0, 2, 26))`
console.log(dtstart.toISOString()) // 2019-10-29T00:02:26.000Z
console.log(dtstart) // Mon Oct 28 2019 17:02:26 GMT-0700 (Pacific Daylight Time)

const fakeDateStart = setPartsToUTCDate(dtstart)
console.log(fakeDateStart.toISOString()) // 2019-10-28T17:02:26.000Z
console.log(fakeDateStart) // Mon Oct 28 2019 10:02:26 GMT-0700 (Pacific Daylight Time)

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

const localUTCOccurrences = rule.all()
console.log(localUTCOccurrences.map(toISOString)) // ["2019-10-29T17:02:26.000Z", "2019-11-05T17:02:26.000Z"]
console.log(localUTCOccurrences) // [Tue Oct 29 2019 10:02:26 GMT-0700 (Pacific Daylight Time), Tue Nov 05 2019 09:02:26 GMT-0800 (Pacific Standard Time)]

const occurrences = localUTCOccurrences.map(setUTCPartsToDate)
console.log(occurrences.map(toISOString)) // ["2019-10-30T00:02:26.000Z", "2019-11-06T01:02:26.000Z"]
console.log(occurrences) // [Tue Oct 29 2019 17:02:26 GMT-0700 (Pacific Daylight Time), Tue Nov 05 2019 17:02:26 GMT-0800 (Pacific Standard Time)]

function setPartsToUTCDate(d) {
  return new Date(
    Date.UTC(
      d.getFullYear(),
      d.getMonth(),
      d.getDate(),
      d.getHours(),
      d.getMinutes(),
      d.getSeconds()
    )
  )
}

function setUTCPartsToDate(d) {
  return new Date(
    d.getUTCFullYear(),
    d.getUTCMonth(),
    d.getUTCDate(),
    d.getUTCHours(),
    d.getUTCMinutes(),
    d.getUTCSeconds()
  )
}

function toISOString(d) {
  return d.toISOString()
}

https://codesandbox.io/s/rrule-localutc-conversion-zxlki

λ˜λŠ” κ°„λ‹¨νžˆ λ§ν•΄μ„œ:

import { RRule } from 'rrule'

const date = new Date('2019-10-29T00:02:26Z')

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

const occurrences = rule.all().map(setUTCPartsToDate)

https://codesandbox.io/s/rrule-localutc-conversion-in-short-ez7g0

λ‚΄κ°€ λ³Ό 수 μžˆλŠ” ν•œ Chrome, Firefox 및 SafariλŠ” λͺ¨λ‘ μ½˜μ†”μ— λ‘œκ·ΈμΈν•  λ•Œ ν˜„μ§€ μ‹œκ°„μ˜ λ¬Έμžμ—΄λ‘œ λ‚ μ§œλ₯Ό ν‘œμ‹œν•˜κ³  Node.jsλŠ” UTC의 ISO λ¬Έμžμ—΄λ‘œ ν‘œμ‹œν•©λ‹ˆλ‹€.

@tim-phillips 저도 μ§€κΈˆ 이 문제둜 고톡받고 있으며 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•œ μΌμ’…μ˜ μ›μˆ­μ΄ 패치λ₯Ό μ°Ύκ³  μžˆμŠ΅λ‹ˆλ‹€.

μ˜€ν›„ 8μ‹œ(동뢀 ν‘œμ€€μ‹œ)에 잘 μž‘λ™ν•˜λŠ” λ˜ν’€μ΄ μ΄λ²€νŠΈκ°€ μžˆμ§€λ§Œ μ˜€ν›„ 9μ‹œ(동뢀 ν‘œμ€€μ‹œ)λŠ” ν•΄μ•Ό ν•˜λŠ” μ „λ‚ λ‘œ λ‹€μ‹œ 미루고 μžˆμŠ΅λ‹ˆλ‹€.

이에 λŒ€ν•œ 닡을 찾을 수 μžˆμ„μ§€ κΈ°λŒ€ν•΄ μ£Όμ„Έμš”.

@tim-phillips ν›Œλ₯­ν•œ κ³ μž₯μž…λ‹ˆλ‹€. 아직 진행 μ€‘μ΄μ§€λ§Œ λ§ˆμΉ¨λ‚΄ μ΄ν•΄ν•˜λŠ” 데 도움이 될 것 κ°™μŠ΅λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€!

@tim-phillips λΆ„μ„ν•΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ λ‹€μŒμ„ μ„€λͺ…ν•  수 μžˆμŠ΅λ‹ˆκΉŒ? μ»΄ν“¨ν„°μ˜ ν˜„μ§€ μ‹œκ°„λŒ€μ— 따라 μ†”λ£¨μ…˜μ΄ μˆ˜μš”μΌμ— λ°˜ν™˜λ  수 μžˆλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€.

image

@tonylau λŠ” μ½˜μ†”μ—μ„œ 확인 ν•˜μ§€ μ•ŠμœΌλ©° 항상 둜컬 μ‹œκ°„λŒ€λ‘œ λ³€ν™˜λ©λ‹ˆλ‹€. getUTC*() λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ‚ μ§œμ˜ 일뢀λ₯Ό 가져와야 ν•©λ‹ˆλ‹€. μ΄λŠ” ν˜„μ§€ μ‹œκ°„λŒ€μ— 관계없이 λ™μΌν•©λ‹ˆλ‹€.

@davidgoli

λ‚΄ μ‹œκ°„λŒ€λ₯Ό UTC+14(Kiritimati Island/Line Islands Time)둜 μ„€μ •ν•˜κ³  getUTCDay()λ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°μ—λ„ λ‹€μŒμ„ μ–»μ—ˆμŠ΅λ‹ˆλ‹€. ν™”μš”μΌμ΄μ–΄μ•Ό ν•  λ‚ μ§œκ°€ μ—¬μ „νžˆ μˆ˜μš”μΌλ‘œ λ°˜ν™˜λ©λ‹ˆκΉŒ?

μƒŒλ“œλ°•μŠ€: https://codesandbox.io/s/rrule-localutc-conversion-in-short-0uzt1

image

이 ν† λ‘ κ³Ό 관련이 μžˆμ„ 수 있음: https://github.com/tc39/proposal-temporal

@νŒ€ν•„λ¦½μŠ€ κ°μ‚¬ν•©λ‹ˆλ‹€!!!!!!!!! λ„ˆλ¬΄ 도움이 λ˜μ—ˆμ–΄μš”!!!!!! 룰둜 ics λ§Œλ“œλŠ” 법을 μ•„μ‹œλ‚˜μš”? λ…Έλ“œμ—μ„œ ics νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•  λ•Œ μš”μΌ λ¬Έμ œκ°€ λ‹€μ‹œ λ°œμƒν•˜κ³  μΊ˜λ¦°λ” μ΄ˆλŒ€λ₯Ό λ°›μœΌλ©΄ νœ΄λ¬΄μž…λ‹ˆλ‹€. (ν•˜μ§€λ§Œ λ‚˜λŠ” λ‹Ήμ‹ μ˜ μ½”λ“œ 덕뢄에 λ‚΄ db에 λͺ¨λ“  것을 μ˜¬λ°”λ₯΄κ²Œ 넣을 수 μžˆμŠ΅λ‹ˆλ‹€!! 정말 κ°μ‚¬ν•©λ‹ˆλ‹€. )

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