RSchedule - это библиотека javascript, написанная на машинописном языке, для работы с повторяющимися датами / событиями. Правила можно импортировать / экспортировать в формате iCal RFC 5545, а сами объекты правил придерживаются протокола итератора javascript.
Пример использования:
const rule = new RRule({
frequency: ‘YEARLY’,
byMonthOfYear: [2, 6],
byDayOfWeek: [‘SU’, [‘MO’, 3]],
start: new Date(2010,1,7),
}, {
dateAdapter: StandardDateAdapter
})
let index = 0;
for (const date of rule.occurrences()) {
date.toISOString()
index++
if (index > 10) break;
}
rule.occurrences({
start: new Date(2010,5,7),
take: 5
})
.toArray()
.map(date => date.toISOString())
rSchedule использует довольно простой объект-оболочку DateAdapter, который абстрагируется от отдельных реализаций библиотеки дат, что делает библиотеку дат rSchedule независимой. Если выбранный вами DateAdapter поддерживает часовые пояса, rSchedule поддерживает часовые пояса.
В настоящее время существуют пакеты StandardDateAdapter, LuxonDateAdapter, MomentDateAdapter и MomentTZDateAdapter, которые предоставляют оболочку, совместимую с DateAdapter, для стандартного объекта Date javascript, а также объектов момент, часовой пояс и люксон. Кроме того, вам должно быть довольно легко создать собственный DateAdapter для предпочитаемой библиотеки.
rSchedule был написан с нуля, чтобы облегчить создание сложных повторяющихся расписаний, и в нем много всего упакованного. Для полного обзора ознакомьтесь с проектом на Gitlab.
Операторы потока событий
Не углубляясь слишком глубоко в библиотеку, я хочу выделить одну очень интересную функцию, которая у нее есть: операторы потока вхождений.
Операторы потока вхождений вдохновлены операторами конвейера rxjs, и они позволяют комбинировать и управлять потоками вхождений из разных объектов.
Пример из моего собственного приложения:
Я хочу получить расписание волонтеров для конкретного волонтера. График волонтеров - это * пересечение * расписания, на которое подписывается человек, и графика появления соответствующей возможности для волонтеров.
Другими словами, у волонтерской возможности есть свой собственный график. Допустим: каждый другой вторник, а также каждую пятницу. Отдельно кто-то может записаться на волонтерство «каждый вторник», а также в несколько конкретных пятниц. Однако дни, когда человек * на самом деле * собирается стать волонтером, являются пересечением этих двух графиков.
Вот как можно построить это новое расписание с помощью rSchedule:
declare const volunteerAvailability: Schedule[];
declare const opportunitySchedule: Calendar;
const volunteerSchedule = new Calendar().pipe(
add(...volunteerAvailability),
unique(),
intersection({
streams: opportunitySchedule
})
);
Разбивая этот пример:
volunteerAvailabilityсодержит массив различных расписаний, когда доброволец указал, что они доступны.opportunityScheduleсодержит календарь, описывающий, когда действительно появляется возможность стать волонтером.
Мы хотим создать календарь, содержащий время, когда волонтер доступен для участия в этой возможности для волонтеров (volunteerSchedule).
const volunteerSchedule =
// create a new calendar
new Calendar().pipe(
// add all times that the volunteer is available, in general
add(...volunteerAvailability),
// filter to get only unique times
unique(),
// get the intersection of these times with the
// volunteer opportunity's schedule
intersection({
streams: opportunitySchedule
})
);
Полученный volunteerSchedule можно повторить с помощью volunteerSchedule.occurrences(). Я также могу легко группировать вхождения по месяцам, используя volunteerSchedule.collections({granularity: 'MONTHLY'}), и перебирать по месяцам.
Учитывая, что у каждого добровольца, который регистрируется, будет свое собственное расписание, я могу затем объединить каждое из этих расписаний вместе, чтобы создать новый календарь, содержащий даты, которые каждый человек должен стать волонтером.
Например:
declare const volunteerSchedules: Calendar[];
const scheduleOfAllVolunteers =
new Calendar({ schedules: volunteerSchedules });
Я полагаю, что большинству пользователей не понадобятся такие мощные инструменты, и для них они могут просто использовать предоставленный объект Schedule, который реализует RRULE, EXRULE, RDATE и EXDATE из ICAL spec (который также упрощает синтаксический анализ / сериализацию строк ICAL).
Например:
const schedule = new Schedule({
rrules: [
{
frequency: ‘WEEKLY’,
start: new Date(2012, 5, 24),
end: new Date(2012, 11, 31)
},
{
frequency: ‘DAILY’,
start: new Date(2011, 9, 2)
}
],
dateAdapter: StandardDateAdapter,
data: ‘Holds anything I want’,
})
schedule
.occurrences({take: 10})
.toArray()
.map(date => date.toISOString())
// or
const schedule = parseICal(icalString);