Skip to content

Instantly share code, notes, and snippets.

@FeMaffezzolli
Created May 11, 2023 18:10
Show Gist options
  • Save FeMaffezzolli/56bd577ac1080234c1b7b78e48b355f4 to your computer and use it in GitHub Desktop.
Save FeMaffezzolli/56bd577ac1080234c1b7b78e48b355f4 to your computer and use it in GitHub Desktop.
Recurring events
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