-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #160 from EthanThatOneKid/fix/143
Refactored iCal parser to parse RRules + Adding recurring label for recurring event
- Loading branch information
Showing
9 changed files
with
290 additions
and
149 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export enum Time { | ||
Millisecond = 1, | ||
Second = 1e3 * Millisecond, | ||
Minute = 60 * Second, | ||
Hour = 60 * Minute, | ||
Day = 24 * Hour, | ||
} | ||
|
||
export const ACM_LOCALE = 'en-US'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import RRule from 'rrule'; | ||
|
||
interface IcalOutput { | ||
[key: string]: string | string[] | IcalOutput[]; | ||
} | ||
|
||
const cleanIcalKey = (key: string): string => { | ||
if (key.startsWith('DTSTART')) return 'DTSTART'; | ||
if (key.startsWith('DTEND')) return 'DTEND'; | ||
return key; | ||
}; | ||
|
||
/** | ||
* The code in this function is derived from | ||
* https://github.com/adrianlee44/ical2json. | ||
* @param source The raw calendar data in ICAL format. | ||
* @returns The parsed ICAL data. | ||
*/ | ||
export const parseRawIcal = (source: string): IcalOutput => { | ||
const output: IcalOutput = {}; | ||
const lines = source.split(/\r\n|\n|\r/); | ||
const parents: IcalOutput[] = []; | ||
let parent: IcalOutput = {}; | ||
let current: IcalOutput = output; | ||
let currentKey = ''; | ||
|
||
for (const line of lines) { | ||
let currentValue = ''; | ||
if (line.charAt(0) === ' ') { | ||
current[currentKey] += line.substr(1); | ||
} else { | ||
const splitAt = line.indexOf(':'); | ||
if (splitAt < 0) { | ||
continue; | ||
} | ||
currentKey = cleanIcalKey(line.substr(0, splitAt)); | ||
currentValue = line.substr(splitAt + 1); | ||
switch (currentKey) { | ||
case 'BEGIN': | ||
parents.push(parent); | ||
parent = current; | ||
if (parent[currentValue] == null) { | ||
parent[currentValue] = []; | ||
} | ||
// Create a new object, store the reference for future uses. | ||
current = {}; | ||
(parent[currentValue] as IcalOutput[]).push(current); | ||
break; | ||
case 'END': | ||
current = parent; | ||
parent = parents.pop() as IcalOutput; | ||
break; | ||
default: | ||
if (current[currentKey]) { | ||
if (!Array.isArray(current[currentKey])) { | ||
current[currentKey] = [current[currentKey]] as string[]; | ||
} | ||
(current[currentKey] as string[]).push(currentValue); | ||
} else { | ||
(current[currentKey] as string) = currentValue; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return output; | ||
}; | ||
|
||
/** | ||
* This function parses the raw ICAL datetime string into a Date object. | ||
* @param datetime Example: October 31st, 2021 = "20211031T000000" | ||
* @returns The parsed Date object. | ||
*/ | ||
const parseRawIcalDatetime = (datetime: string): Date => { | ||
const fullYear = datetime.slice(0, 4); | ||
const month = datetime.slice(4, 6); | ||
const day = datetime.slice(6, 8); | ||
const hours = datetime.slice(9, 11); | ||
const minutes = datetime.slice(11, 13); | ||
const seconds = datetime.slice(13, 15); | ||
const date = new Date(); | ||
date.setFullYear(Number(fullYear), Number(month) - 1, Number(day)); | ||
date.setHours(Number(hours) - 7, Number(minutes), Number(seconds)); | ||
return date; | ||
}; | ||
|
||
export const computeIcalDatetime = (event: any) => { | ||
const rawDatetime = event['DTSTART']; | ||
const rawRrule = event['RRULE']; | ||
if (rawRrule !== undefined) { | ||
try { | ||
const ruleSrc = `DTSTART:${rawDatetime}Z\nRRULE:${rawRrule}`; | ||
const recurrence = RRule.fromString(ruleSrc); | ||
const date = recurrence.after(new Date()); | ||
date.setHours(date.getHours()); | ||
return date; | ||
} catch {} | ||
} | ||
return parseRawIcalDatetime(rawDatetime); | ||
}; | ||
|
||
export const slugifyEvent = (summary: string, month: string, day: number): string => { | ||
const cleanedSummary = summary.replace(/[^\w\s\_]/g, '').replace(/(\s|\-|\_)+/g, '-'); | ||
const slug = [cleanedSummary, month, day].join('-').toLowerCase(); | ||
return slug; | ||
}; | ||
|
||
export const checkForRecurrence = (raw?: string): boolean => { | ||
if (raw === undefined) return false; | ||
try { | ||
const recurrence = RRule.fromString(raw); | ||
return recurrence.isFullyConvertibleToText(); | ||
} catch {} | ||
return false; | ||
}; | ||
|
||
export const parseDescription = (content: string): Record<string, string> => { | ||
const result = {}; | ||
for (const line of content.split('\n')) { | ||
const [key, value] = line.split('='); | ||
result[key.trim()] = value; | ||
} | ||
return result; | ||
}; | ||
|
||
export const sortByDate = () => { | ||
return ({ date: date1 }, { date: date2 }) => (date1.valueOf() > date2.valueOf() ? 1 : -1); | ||
}; | ||
|
||
export const filterIfPassed = (now: number, offset: number = 0) => { | ||
return ({ date }: { date: Date }) => date.valueOf() + offset > now; | ||
}; |
Oops, something went wrong.
eab5d5f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: