Привет, Хабр! Меня зовут Денис Басковский. Я фронтенд-разработчик в билетном сервисе Ticketland.ru (принадлежит МТС Live). В одном из пет-проектов мне понадобилось управлять календарными событиями: ставить время начала и окончания, добавлять комментарии и посылать оповещения. Обычно такая информация хранится и передается в .ics-файлах, описанных в спецификации iCalendar. Благодаря этому формату многие современные календарные приложения могут синхронизировать данные между собой.
В этой статье я расскажу об особенностях этой спецификации, приведу примеры использования .ics-файлов и поделюсь своей библиотекой на TypeScript для их генерации. Все подробности — под катом.
Что может iCalendar
Унификация работы с календарями прошла несколько этапов:
Первый шаг сделала в 1996 году компания Versit Consortium, которая разработала стандарт vCalendar.
В 1998 году IETF опубликовал стандарт RFC 2445, основанный на опыте использования vCalendar.
В 2009 году на замену RFC 2445 появились RFC 5545 и его расширение iCalendar протоколом iTIP, описанным в RFC 5546.
В 2012 году RFC 6638 добавил поддержку планирования (scheduling) в CalDAV на основе протокола iTIP.
Из достоинств этого формата можно отметить:
Универсальность. Сейчас iCalendar используется всеми современными календарными приложениями и сервисами: Microsoft Outlook, Google Calendar, Apple Calendar и так далее. Он обеспечивает им совместимость и позволяет синхронизироваться.
Легкость обмена данными. Синхронизация между разными приложениями происходит с помощью экспорта и импорта .ics-файлов.
Гибкость. iCalendar поддерживает сложные сценарии использования: повторяющиеся события, задачи с приоритетами, уведомления.
Поддержка протокола CalDAV. Позволяет организовать работу с календарными данными в распределенных системах, обеспечивая синхронизацию и управление событиями между разными устройствами и приложениями через HTTP-запросы к серверу, поддерживающему CalDAV. Подробно об этом расскажу ниже.
Нужно учитывать, что при создании сложных повторяющихся событий с большим числом исключений структура ics-файла будет усложняться, а размер файла увеличиваться.
Что входит в файл календаря
Пример .ics-файла:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//id//NONSGML v1.0//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
UID:12345678-1234-1234-1234-123456789012
DTSTART:20240816T150000Z
SUMMARY:title
DESCRIPTION:description
END:VEVENT
END:VCALENDAR
Файлы .ics используют текстовые данные и включают компоненты, каждый из которых служит для своей цели. Вот они:
VJOURNAL хранит записи в журнале. С его помощью можно вести личные заметки, записывать события в течение дня.
VEVENT описывает события или встречи и включает дату и время начала и окончания, местоположение, информации об организаторе и участниках, он также может содержать информацию о повторениях (RRULE), статус события, категорию и пр. Применяется для создания событий в календаре, отправки и получения откликов.
VTODO описывает параметры задачи: срок выполнения, приоритет, текущий статус и прогресс.
VALARM настраивает оповещения, связанные с VEVENT или VTODO. С его помощью можно завести оповещение, настроить тип, время и регулярность уведомления.
VFREEBUSY используется для описания периодов свободного или занятого времени.
Пример использования
В распределенных системах клиентские приложения — например, календари на мобильных устройствах или десктопах — работают с событиями на серверах через протокол CalDAV и сохраняют полную совместимость с форматом iCalendar.
Google Calendar использует CalDAV для синхронизации с внешними клиентами, поддерживающими этот протокол. А Google Calendar API, хоть и использует собственную структуру данных для работы с событиями, поддерживает стандарт iCalendar. Так появляется возможность синхронизироваться с клиентами, которые работают только c iCalendar.
Microsoft Outlook поддерживает импорт и экспорт .ics-файлов, а это позволяет обмениваться событиями с другими календарными системами. Microsoft Exchange Server использует .ics-файлы для обмена событиями и задачами между Outlook и другими клиентами.
Как работать с .ics-файлами
У .ics-файла текстовый тип и mime-type text/calendar. В самом простом варианте его можно создать вручную в текстовом редакторе и проверить на ошибки с помощью любого стороннего валидатора, поддерживающего RFC 5545.
Другой вариант создания .ics-файлов — использовать готовые библиотеки. Для JavaScript я нашел две:
icalendar.js — заброшенный проект, последняя правка была десять лет назад. Функционирует только в Nodejs, требует полифилы для работы в браузере.
rrule.js — более живой проект, специализирующийся на генерации и управлении повторяющимися событиями (RRULE). Эта библиотека предназначена для использования как в Node.js, так и в браузерах, однако для создания полных .ics-файлов могут потребоваться дополнительные библиотеки. Размер библиотеки 687 Кб, есть зависимость пакета tslib.
В своем пет-проекте я использую стандарт RFC 5545. Так как готовые варианты библиотек меня не устроили, я реализовал создание ics-файлов в собственной библиотеке ical-browser на TypeScript. В отличие от rrule.js и icalendar.js библиотека работает в том числе и в браузерах без создания тяжелых зависимостей. У меня много планов по ее доработке — пишите в личку, если хотите помочь или нужна какая-то дополнительная функциональность.
Пример работы с ical-browser
Сначала установим пакет:
npm install ical-browser
Подключим библиотеку к своему проекту, поддерживающему ESM-модули. Работать будем с типами Event, Todo, Journal и Alarm:
import { todo as createTodo,
event as createEvent,
journal as createJournal,
alarm as createAlarm,
default as icalendar, } from 'ical-browser'
// Раньше можно было использовать любое значение, но для повышения безопасности требуется уникальное значение идентификатора, например uuidv1
const uid = 'c7614cff-3560-4a00-9152-d25cc1fe077d',
const event = createEvent({
uid,
location: 'Online',
geo: [37.5739497,-85.7399606],
summary: 'Event summary',
description: 'Event description',
stamp: new Date(),
start: new Date('2024-01-01T10:10:00.611Z'),
end: new Date('2024-01-02T10:12:00.611Z'),
// Прикладываем документ в формате base64. В данном случае это будет пиксель в формате gif
attach: [
'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQEAAAAACwAAAAAAQABAAACAkQBADs=',
],
organizer: 'Jane Doe',
attendee: 'CN=John Smith:mailto:john.smith@example.com',
url: new URL('https://example.com#event'),
})
const todo = createTodo({
uid,
stamp: new Date(),
due: new Date(),
summary: 'Task summary',
description: 'Task description',
priority: 1,
status: 'CONFIRMED',
})
const journal = createJournal({
uid,
stamp: new Date(),
due: new Date(),
summary: 'Journal summary',
description: 'Journal description',
})
const alarm = createAlarm({
uid,
trigger: '-PT5M',
description: 'Reminder',
action: 'DISPLAY',
})
// Cклеиваем данные и генерируем файл
const str = icalendar('ID’, { event, todo, journal, alarm })
const file = new File([new TextEncoder().encode(str)], 'example.ics', {
type: 'text/calendar',
})
После запуска пробуем открыть созданный файл:
Важно учитывать, что время берется с указанием таймзоны. Поэтому в моем примере значением new Date('2024-01-01T10:10:00.611Z') для Москвы станет 13:10.
Итог
iCalendar — это гибкий и мощный стандарт для работы с календарными данными, который поддерживается большинством современных приложений и сервисов. Файлы .ics легко создаются и импортируются во все самые популярные приложения для управления временем.
Надеюсь, моя статья помогла вам лучше понять стандарт iCalendar и то, как интегрировать его в свои проекты. Если у вас возникли вопросы или есть чем дополнить статью — пишите в комментариях.