Created
May 11, 2023 18:10
-
-
Save FeMaffezzolli/56bd577ac1080234c1b7b78e48b355f4 to your computer and use it in GitHub Desktop.
Recurring events
This file contains hidden or 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
import { DateTime, Duration } from 'luxon' | |
import { v4 as uuid } from 'uuid' | |
interface RecurringRule { | |
interval?: keyof Duration; | |
intervalCount?: number; | |
byWeekDay?: number[]; | |
byMonthDay?: number[]; | |
count?: number; | |
until?: string; | |
} | |
interface RecurringEvent { | |
id: string; | |
previousEventId?: string; | |
title: string; | |
startDateTime: string; | |
endDateTime: string; | |
eventDurationInHours: number; | |
rule?: RecurringRule; | |
} | |
/** | |
* Generate events based on a recurring rule | |
*/ | |
export function generateEquallySpacedRecurringEvents (event: Omit<RecurringEvent, 'eventDurationInHours'>): RecurringEvent[] { | |
const events: RecurringEvent[] = [] | |
const { | |
title, | |
startDateTime, | |
endDateTime, | |
rule, | |
} = event | |
if (!rule) return events | |
if (!rule.interval) throw new Error('Invalid recurring rule. Missing interval.') | |
if (!rule.count && !rule.until) throw new Error('Invalid recurring rule. Missing count or until.') | |
let count = 0 | |
let latestEvent = event | |
let current = DateTime.fromISO(startDateTime) | |
const eventDurationInHours = DateTime.fromISO(endDateTime).diff(DateTime.fromISO(startDateTime), 'hours').as('hours') | |
while (true) { | |
if (rule.count && count >= rule.count) break | |
if (rule.until && current >= DateTime.fromISO(rule.until)) break | |
const newEvent = { | |
id: uuid(), | |
title: title + ' ' + (count + 1), | |
rule, | |
previousEventId: latestEvent.id, | |
startDateTime: current.toUTC().toISO(), | |
endDateTime: current.plus({ hours: eventDurationInHours }).toUTC().toISO(), | |
eventDurationInHours, | |
} | |
latestEvent = newEvent | |
events.push(newEvent) | |
current = current.plus({ [rule.interval]: rule.intervalCount }) | |
count++ | |
} | |
return events | |
} | |
/** | |
* Generate events based on specif month day index or day of week | |
*/ | |
export function generateUnequallySpacedRecurringEvents (event: Omit<RecurringEvent, 'eventDurationInHours'>): RecurringEvent[] { | |
const events: RecurringEvent[] = [] | |
const { | |
title, | |
startDateTime, | |
endDateTime, | |
rule, | |
} = event | |
if (!rule) return events | |
if (!rule.count && !rule.until) throw new Error('Invalid recurring rule. Missing count or until.') | |
if (!rule.byWeekDay && !rule.byMonthDay) throw new Error('Invalid recurring rule. Missing count or until.') | |
let count = 0 | |
let latestEvent = event | |
let current = DateTime.fromISO(startDateTime) | |
const eventDurationInHours = DateTime.fromISO(endDateTime).diff(DateTime.fromISO(startDateTime), 'hours').as('hours') | |
while (true) { | |
if (rule.count && count >= rule.count) break | |
if (rule.until && current >= DateTime.fromISO(rule.until)) break | |
if (rule.byWeekDay && rule.byWeekDay.includes(current.weekday)) { | |
const newEvent = { | |
id: uuid(), | |
title: title + ' ' + (count + 1), | |
rule, | |
previousEventId: latestEvent.id, | |
startDateTime: current.toUTC().toISO(), | |
endDateTime: current.plus({ hours: eventDurationInHours }).toUTC().toISO(), | |
eventDurationInHours, | |
} | |
latestEvent = newEvent | |
events.push(newEvent) | |
count++ | |
} else if (rule.byMonthDay && rule.byMonthDay.includes(current.day)) { | |
const newEvent = { | |
id: uuid(), | |
title: title + ' ' + (count + 1), | |
rule, | |
previousEventId: latestEvent.id, | |
startDateTime: current.toUTC().toISO(), | |
endDateTime: current.plus({ hours: eventDurationInHours }).toUTC().toISO(), | |
eventDurationInHours, | |
} | |
latestEvent = newEvent | |
events.push(newEvent) | |
count++ | |
} | |
current = current.plus({ day: 1 }) | |
} | |
return events | |
} | |
const ESEvents = generateEquallySpacedRecurringEvents({ | |
startDateTime: '2023-04-28T10:00:00.000Z', | |
endDateTime: '2023-04-28T12:00:00.000Z', | |
title: 'ESE', | |
id: '1', | |
rule: { | |
interval: 'months', | |
intervalCount: 1, | |
count: 4, | |
// until: '2023-06-28T10:00:00.000Z', | |
}, | |
}) | |
console.log(ESEvents) | |
console.log(` | |
Generated ${ESEvents.length} events | |
The first event starts at ${ESEvents[0].startDateTime} | |
The last event starts at ${ESEvents[ESEvents.length - 1].startDateTime} | |
`) | |
const USEvents = generateUnequallySpacedRecurringEvents({ | |
startDateTime: '2023-04-28T10:00:00.000Z', | |
endDateTime: '2023-04-28T12:00:00.000Z', | |
title: 'USE', | |
id: '1', | |
rule: { | |
byWeekDay: [4, 5], | |
byMonthDay: [1, 15], | |
// count: 8, | |
// byMonthDay: [1, 15], | |
// until: '2023-05-28T10:00:00.000Z', | |
}, | |
}) | |
console.log(USEvents) | |
console.log(` | |
Generated ${USEvents.length} events | |
The first event starts at ${USEvents[0].startDateTime} | |
The last event starts at ${USEvents[USEvents.length - 1].startDateTime} | |
`) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment