Афиша на странице сайта очевидна человеку: название концерта, 18 апреля, начало в 19:00, адрес зала. Робот видит просто текст, в котором дата ничем не отличается от номера ряда. Микроданные закрывают этот разрыв: в HTML рядом с видимым текстом появляются атрибуты, превращающие строки в строгие пары "имя свойства - значение". Словарь schema.org описывает тип Event для событий, и календарные сервисы, парсеры агрегаторов и поисковые роботы читают такие пары без единой догадки. Ниже разобран синтаксис на живых примерах, обязательные поля, вложенные сущности, статусы переноса и расписания повторяющихся встреч.
Механика микроданных в HTML и место словаря schema.org
Микроданные входят в живой стандарт HTML, который поддерживает консорциум WHATWG. Идея проста: у обычных элементов страницы появляются атрибуты, и разметка начинает рассказывать о себе сама. Атрибут itemscope объявляет "предмет", то есть самостоятельную сущность. Атрибут itemtype указывает адрес типа в словаре. Атрибут itemprop помечает потомка предмета как свойство со значением.
Значением свойства обычно становится текст внутри элемента, но для отдельных тегов есть приоритетный поставщик значения: у тега time оно берётся из атрибута datetime, у тега meta из атрибута content, у link и a из href. Атрибут itemid закрепляет глобальный идентификатор предмета, а itemref подтягивает свойства из дальних уголков документа по списку id элементов.
Ранние редакции стандарта описывали и JavaScript API для чтения предметов прямо из браузера, но из браузеров тот интерфейс убрали. Разбором микроданных сегодня занимаются внешние парсеры, валидаторы и поисковые роботы. Словарь schema.org развивается с 2011 года: тип Event наследует поля от Thing и уточняется более чем двадцатью подтипами, от MusicEvent и TheaterEvent до SportsEvent и Festival.
Минимальная разметка события выглядит так:
<article itemscope itemtype="https://schema.org/Event">
<h1 itemprop="name">Лекция про внутренности браузерных движков</h1>
<p itemprop="description">Разбор конвейера рендеринга на конкретных примерах кода.</p>
<p>Начало:
<time itemprop="startDate" datetime="2026-04-18T19:00+03:00">
18 апреля 2026 года в 19:00
</time>
</p>
</article>
Человек читает заголовок и абзац, машина получает три пары: name, description и startDate с датой в нормализованном виде. Копия данных не создаётся, разметка и контент физически не расходятся.
Обязательные поля Event и формат дат по ISO 8601
Для попадания события в расширенные результаты поиска обязательны три поля: name, startDate и location. Поле name несёт полное название события. Сюда не попадают ни название площадки, ни цена, ни призыв успеть за билетом: для цен и адресов есть отдельные свойства, а название площадки относится в location.name.
Дата в startDate записывается по ISO 8601, и это ключ к календарям. Человек видит "18 апреля 2026 года в 19:00", а машина читает атрибут datetime со значением 2026-04-18T19:00+03:00, где явно заданы год, месяц, день, час, минута и смещение часового пояса. Без смещения робот обязан гадать, в каком поясе идёт событие, и напоминание в календаре жителя другого региона съедет на несколько часов. Пример с полным набором полей на двухдневный фестиваль:
<article itemscope itemtype="https://schema.org/Event">
<h1 itemprop="name">Фестиваль синтезаторной музыки</h1>
<p>Программа начинается
<time itemprop="startDate" datetime="2026-06-06T12:00+03:00">6 июня в 12:00</time>
и заканчивается
<time itemprop="endDate" datetime="2026-06-07T22:00+03:00">7 июня в 22:00</time>.
</p>
</article>
Поле endDate рекомендуется заполнять тем же форматом, а для многодневных фестивалей допустима запись только даты без времени. Полезные соседи обязательной тройки: doorTime со временем открытия дверей, duration в нотации ISO 8601, где PT2H30M читается как два с половиной часа, typicalAgeRange с возрастной маркировкой вроде "18+", isAccessibleForFree для бесплатных событий и inLanguage с языком программы.
Место проведения и почтовый адрес во вложенной сущности Place
Свойство location принимает четыре типа значений: Place для площадки, PostalAddress для адреса в чистом виде, VirtualLocation для онлайн-события и Text для запасного варианта. Вложенная сущность строится повторением пары itemscope itemtype внутри родительского блока, и так растёт дерево из события, площадки и адреса:
<div itemprop="location" itemscope itemtype="https://schema.org/Place">
<span itemprop="name">Дворец культуры железнодорожников</span>
<div itemprop="address" itemscope itemtype="https://schema.org/PostalAddress">
<span itemprop="streetAddress">улица Станционная, 7</span>,
<span itemprop="addressLocality">Самара</span>,
<span itemprop="postalCode">443100</span>,
<span itemprop="addressCountry">RU</span>
</div>
</div>
У типа PostalAddress адрес лежит по полям: streetAddress, addressLocality, postalCode, addressRegion, addressCountry. Вместо одного слова "Сидней" стоит указывать полный адрес, потому что по городу робот не найдёт ни зала, ни нужную дверь. Если событие идёт в нескольких точках одновременно, для каждой заводится отдельная карточка Event. Для трансляции площадки нет, и тогда в location кладут VirtualLocation с полем url на комнату трансляции. Когда шаблон разносит название и адрес по разным углам страницы, выручает itemref: свойства из перечисленных в нём id приписываются предмету, даже если элементы лежат за пределами блока события.
Билеты, цены и доступность через вложенный тип Offer
Свойство offers принимает вложенный тип Offer, по одному на каждый вид билета. У билета есть цена, валюта, доступность и момент старта продаж. Цена кладётся в поле price числом, включая сборы и сервисные комиссии, валюта в priceCurrency кодом из трёх букв по ISO 4217, например RUB или EUR. Доступность берётся из перечисления ItemAvailability: InStock для билетов в наличии, SoldOut для распроданных, PreOrder для предзаказа. Бесплатное событие не остаётся без поля, а получает price со значением 0:
<div itemprop="offers" itemscope itemtype="https://schema.org/Offer">
<p>Билет:
<span itemprop="price">1200</span>
<meta itemprop="priceCurrency" content="RUB">
</p>
<p>
<link itemprop="availability" href="https://schema.org/InStock">
Есть в продаже с 1 марта
</p>
<meta itemprop="validFrom" content="2026-03-01T10:00+03:00">
</div>
Значения перечислений всегда передаются адресами, поэтому availability помечается через link с href, а не текстом внутри span. Организатор и исполнители строятся по той же схеме: organizer и performer принимают Person или Organization, и для каждого хватает своего itemscope с name. Поле image набирает несколько картинок: рекомендованная ширина 1920 пикселей, минимум 720, соотношения сторон 16x9, 4x3 и 1x1, а площадь каждой не меньше 50 тысяч пикселей при перемножении сторон.
Статусы события и режимы посещения в перечислениях EventStatusType
У события есть жизнь после публикации: его отменяют, переносят или переводят в онлайн. Поле eventStatus принимает значения из перечисления EventStatusType, которое описывает пять сценариев:
1. EventScheduled - событие идёт по плану, а это значение по умолчанию, когда поле вообще не заполнено;
2. EventCancelled - событие отменено, при этом прежние даты и площадка остаются в разметке без изменений;
3. EventPostponed - событие отложено, новая дата неизвестна, поэтому в startDate остаётся прежняя дата;
4. EventRescheduled - событие перенесено на конкретную дату, новые значения уходят в startDate и endDate, а старая дата в previousStartDate;
5. EventMovedOnline - очное событие переведено в дистанционный формат, площадка меняется на VirtualLocation.
Главное правило при смене статуса звучит неожиданно: startDate выбрасывать нельзя, даже когда событие отменено. Дата нужна роботам, чтобы опознать конкретную встречу и показать новый статус вместо исчезнувшей карточки. Пример отменённой лекции:
<article itemscope itemtype="https://schema.org/Event" itemid="https://example.org/events/lecture-42">
<h1 itemprop="name">Лекция</h1>
<p>
<time itemprop="startDate" datetime="2026-04-18T19:00+03:00">18 апреля, 19:00</time>
<link itemprop="eventStatus" href="https://schema.org/EventCancelled">
</p>
</article>
Второй маяк для роботов - eventAttendanceMode с тремя значениями: OfflineEventAttendanceMode для очного, OnlineEventAttendanceMode для дистанционного и MixedEventAttendanceMode для гибридного формата. На гибридных встречах число мест ограничивают полями maximumPhysicalAttendeeCapacity и maximumVirtualAttendeeCapacity, а общий потолок задаёт maximumAttendeeCapacity. Комбинация статуса и режима посещения покрывает почти все повороты жизни события.
Повторяющиеся события через свойство eventSchedule и тип Schedule
Регулярные занятия плохо ложатся на одиночные карточки: недельный курс йоги, шахматный кружок по субботам, стендап по средам. У типа Event есть свойство eventSchedule со вложенным типом Schedule, и у этой пары жёсткое правило: событие с расписанием не содержит собственных полей startDate и endDate, даты переезжают внутрь расписания, иначе читатели разметки получают две версии правды.
Внутри Schedule обязательный минимум одно поле repeatFrequency, интервал между повторами в нотации ISO 8601: P1W задаёт еженедельный ритм, P1D ежедневный, P2W через неделю. Дни недели собираются в byDay, время начала и конца в startTime и endTime, период активности в startDate и endDate самого расписания, а исключения в exceptDate. Расписание утренней йоги на сезон выглядит так:
<div itemscope itemtype="https://schema.org/Event">
<h1 itemprop="name">Утренняя йога для начинающих</h1>
<div itemprop="eventSchedule" itemscope itemtype="https://schema.org/Schedule">
<meta itemprop="startDate" content="2026-02-02">
<meta itemprop="endDate" content="2026-05-25">
<meta itemprop="repeatFrequency" content="P1W">
<link itemprop="byDay" href="https://schema.org/Monday">
<meta itemprop="startTime" content="10:00">
<meta itemprop="endTime" content="11:00">
<meta itemprop="scheduleTimezone" content="Europe/Moscow">
</div>
</div>
Поле scheduleTimezone принимает имя зоны из базы IANA и спасает при переводе часов: занятие на 10:00 остаётся в 10:00 в своей зоне круглый год. Расписаний у одного события допускается несколько, например отдельное на каждый сезон. Полезные соседи: repeatCount с числом повторов и exceptDate с датами пропусков. Агрегаторы разворачивают такое расписание в серию встреч сами, и автору не нужно плодить десятки одинаковых карточек.
Проверка микроданных валидаторами и типичные ошибки разметки
Для микроданных работают те же инструменты, что и для JSON-LD. Валидатор словаря schema.org вытаскивает предметы из готовой страницы и показывает дерево свойств: если itemtype написан с опечаткой или itemprop оказался вне своего itemscope, дерево разваливается на глазах. Тест расширенных результатов проверяет пригодность разметки для поисковой выдачи и отсеивает критичные ошибки. У микроданных есть родное преимущество: разметка лежит внутри видимого контента, поэтому шаблон физически не покажет человеку одну цену, а машине другую. JSON-LD живёт в отдельном теге script и удобен динамическим сайтам, а требования поиска для событий допускают оба синтаксиса наравне с RDFa.
Типичные ошибки собираются вокруг одних и тех же граблей. Дата текстом "18 апреля" вместо ISO 8601 ломает календарную логику. Отсутствие смещения пояса сдвигает напоминание на часы. Название площадки в поле name превращает карточку в безымянную. Город в поле адреса вместо улицы и индекса оставляет событие без точки на карте. Значение перечисления текстом вместо href в link читается как строка, а не как статус из словаря. Выброшенный startDate при отмене лишает событие идентификации, и выдача продолжает показывать отменённую встречу как живую. Расписание без repeatFrequency валидатор отметит первым.
Рабочий путь выглядит скучно и надёжно: разметить обязательный минимум, добавить вложенные Place и Offer, проставить статус и режим посещения, свериться с деревом валидатора и выкладывать страницу. Человек нажимает кнопку добавления в календарь и получает точное время со своим поясом, точный адрес и цену, собранные не из догадок, а из полей разметки. Для сайта это бесплатный мост между афишей и миром машин, которые понимают только атрибуты словаря.