UNPKG

@heinlein-video/rrule

Version:

rrule fork. Includes the src/ folder for typescript sourceMaps

221 lines (192 loc) 5.99 kB
import { Options, ParsedOptions, freqIsDailyOrGreater } from './types' import { includes, notEmpty, isPresent, isNumber, isArray, isWeekdayStr, } from './helpers' import { RRule, defaultKeys, DEFAULT_OPTIONS } from './rrule' import { getWeekday, isDate, isValidDate } from './dateutil' import { Weekday } from './weekday' import { Time } from './datetime' export function initializeOptions(options: Partial<Options>) { const invalid: string[] = [] const keys = Object.keys(options) as (keyof Options)[] // Shallow copy for options and origOptions and check for invalid for (const key of keys) { if (!includes(defaultKeys, key)) invalid.push(key) if (isDate(options[key]) && !isValidDate(options[key])) { invalid.push(key) } } if (invalid.length) { throw new Error('Invalid options: ' + invalid.join(', ')) } return { ...options } } export function parseOptions(options: Partial<Options>) { const opts = { ...DEFAULT_OPTIONS, ...initializeOptions(options) } if (isPresent(opts.byeaster)) opts.freq = RRule.YEARLY if (!(isPresent(opts.freq) && RRule.FREQUENCIES[opts.freq])) { throw new Error(`Invalid frequency: ${opts.freq} ${options.freq}`) } if (!opts.dtstart) opts.dtstart = new Date(new Date().setMilliseconds(0)) if (!isPresent(opts.wkst)) { opts.wkst = RRule.MO.weekday } else if (isNumber(opts.wkst)) { // cool, just keep it like that } else { opts.wkst = opts.wkst.weekday } if (isPresent(opts.bysetpos)) { if (isNumber(opts.bysetpos)) opts.bysetpos = [opts.bysetpos] for (let i = 0; i < opts.bysetpos.length; i++) { const v = opts.bysetpos[i] if (v === 0 || !(v >= -366 && v <= 366)) { throw new Error( 'bysetpos must be between 1 and 366,' + ' or between -366 and -1' ) } } } if ( !( Boolean(opts.byweekno as number) || notEmpty(opts.byweekno as number[]) || notEmpty(opts.byyearday as number[]) || Boolean(opts.bymonthday) || notEmpty(opts.bymonthday as number[]) || isPresent(opts.byweekday) || isPresent(opts.byeaster) ) ) { switch (opts.freq) { case RRule.YEARLY: if (!opts.bymonth) opts.bymonth = opts.dtstart.getUTCMonth() + 1 opts.bymonthday = opts.dtstart.getUTCDate() break case RRule.MONTHLY: opts.bymonthday = opts.dtstart.getUTCDate() break case RRule.WEEKLY: opts.byweekday = [getWeekday(opts.dtstart)] break } } // bymonth if (isPresent(opts.bymonth) && !isArray(opts.bymonth)) { opts.bymonth = [opts.bymonth] } // byyearday if ( isPresent(opts.byyearday) && !isArray(opts.byyearday) && isNumber(opts.byyearday) ) { opts.byyearday = [opts.byyearday] } // bymonthday if (!isPresent(opts.bymonthday)) { opts.bymonthday = [] opts.bynmonthday = [] } else if (isArray(opts.bymonthday)) { const bymonthday = [] const bynmonthday = [] for (let i = 0; i < opts.bymonthday.length; i++) { const v = opts.bymonthday[i] if (v > 0) { bymonthday.push(v) } else if (v < 0) { bynmonthday.push(v) } } opts.bymonthday = bymonthday opts.bynmonthday = bynmonthday } else if (opts.bymonthday < 0) { opts.bynmonthday = [opts.bymonthday] opts.bymonthday = [] } else { opts.bynmonthday = [] opts.bymonthday = [opts.bymonthday] } // byweekno if (isPresent(opts.byweekno) && !isArray(opts.byweekno)) { opts.byweekno = [opts.byweekno] } // byweekday / bynweekday if (!isPresent(opts.byweekday)) { opts.bynweekday = null } else if (isNumber(opts.byweekday)) { opts.byweekday = [opts.byweekday] opts.bynweekday = null } else if (isWeekdayStr(opts.byweekday)) { opts.byweekday = [Weekday.fromStr(opts.byweekday).weekday] opts.bynweekday = null } else if (opts.byweekday instanceof Weekday) { if (!opts.byweekday.n || opts.freq > RRule.MONTHLY) { opts.byweekday = [opts.byweekday.weekday] opts.bynweekday = null } else { opts.bynweekday = [[opts.byweekday.weekday, opts.byweekday.n]] opts.byweekday = null } } else { const byweekday: number[] = [] const bynweekday = [] for (let i = 0; i < opts.byweekday.length; i++) { const wday = opts.byweekday[i] if (isNumber(wday)) { byweekday.push(wday) continue } else if (isWeekdayStr(wday)) { byweekday.push(Weekday.fromStr(wday).weekday) continue } if (!wday.n || opts.freq > RRule.MONTHLY) { byweekday.push(wday.weekday) } else { bynweekday.push([wday.weekday, wday.n]) } } opts.byweekday = notEmpty(byweekday) ? byweekday : null opts.bynweekday = notEmpty(bynweekday) ? bynweekday : null } // byhour if (!isPresent(opts.byhour)) { opts.byhour = opts.freq < RRule.HOURLY ? [opts.dtstart.getUTCHours()] : null } else if (isNumber(opts.byhour)) { opts.byhour = [opts.byhour] } // byminute if (!isPresent(opts.byminute)) { opts.byminute = opts.freq < RRule.MINUTELY ? [opts.dtstart.getUTCMinutes()] : null } else if (isNumber(opts.byminute)) { opts.byminute = [opts.byminute] } // bysecond if (!isPresent(opts.bysecond)) { opts.bysecond = opts.freq < RRule.SECONDLY ? [opts.dtstart.getUTCSeconds()] : null } else if (isNumber(opts.bysecond)) { opts.bysecond = [opts.bysecond] } return { parsedOptions: opts as ParsedOptions } } export function buildTimeset(opts: ParsedOptions) { const millisecondModulo = opts.dtstart.getTime() % 1000 if (!freqIsDailyOrGreater(opts.freq)) { return [] } const timeset: Time[] = [] opts.byhour.forEach((hour) => { opts.byminute.forEach((minute) => { opts.bysecond.forEach((second) => { timeset.push(new Time(hour, minute, second, millisecondModulo)) }) }) }) return timeset }