
Привет, Хабр! Меня зовут Денис Басковский. Я фронтенд-разработчик в билетном сервисе 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 и то, как интегрировать его в свои проекты. Если у вас возникли вопросы или есть чем дополнить статью — пишите в комментариях.
Комментарии (4)
 - aleksejs103.09.2024 06:42- Писать свою либу для этого - это сложная затея. В примерах вы генерируете файлы, а потом читаете их же. Но в рельной жизни, вы генерируете файлы, а читает их кто-то другой (это ещё не страшно), а вот когда генерирует файлы кто-то другой, а вашей либе надо их прочитать - вот тут начинается веселье. - Календари крупных компаний вообще ушли от этих стандартов, и у них для синхронизаций есть собственные нативные API. Да, они поддерживают импорт и экспорт, но и у них не всё так гладко. При переносе ивентов с айфона на андройд или обратно, могут появляться артефакты. - А данные, врутри этих файлов далеко уходят за спецификацию календарей. Чего только стоит одно только переименование Kiev в Kyiv. Ещё не факт что вам нормально удастся читать все часовые поеса всех операционных систем.  - qertis Автор03.09.2024 06:42- Согласен, надстройки свои у каждого производителя, тот же MS Office использует свой собственный стандарт. 
 Для чтения стандартных iCal файлов могу порекомендовать библиотеку ical.js.
 
 
           
 
format1981
Дата публикации поста не случайно была выбрана?
DvoiNic
И где раздел VROTATE ? :-)