Соответствие токенов при разборе

Разбор строк в формате даты и времени в Luxon основан на строгом сопоставлении входной строки с набором форматных токенов. Каждый токен представляет собой атомарное правило интерпретации символов, и вся операция парсинга сводится к последовательному совпадению этих правил с текстом.

Токенизация формата выполняет две ключевые функции: определение границ значений и интерпретацию их семантики. При несовпадении хотя бы одного токена с соответствующим фрагментом строки результатом становится невалидная дата.


Механизм сопоставления форматной строки и входных данных

Форматная строка в Luxon представляет собой последовательность токенов, каждый из которых соответствует определённому компоненту даты или времени.

Пример:

DateTime.fromFormat("2026-01-25 14:30", "yyyy-MM-dd HH:mm");

Здесь строка разбивается на логические сегменты:

  • yyyy → год (4 цифры)
  • MM → месяц (2 цифры)
  • dd → день
  • HH → часы (24-часовой формат)
  • mm → минуты

Разбор выполняется слева направо без обратного поиска. Каждый символ входной строки должен быть объяснён соответствующим токеном.


Жёсткое соответствие длины токенов

Одним из фундаментальных правил является зависимость поведения парсинга от длины токена.

Годовые токены

  • y — минимальная длина (1–6 цифр)
  • yy — две цифры
  • yyyy — строго 4 цифры
DateTime.fromFormat("26", "yy");      // 2026
DateTime.fromFormat("2026", "yyyy");  // 2026

Если формат требует фиксированной длины, несоответствие длины приводит к неуспешному разбору.


Месяцы и дни

  • M — 1–2 цифры (1–12)
  • MM — строго 2 цифры
  • d — 1–2 цифры
  • dd — строго 2 цифры
DateTime.fromFormat("3", "MM"); // невалидно
DateTime.fromFormat("03", "MM"); // валидно

Сопоставление не выполняет автоматического дополнения нулями при строгих токенах.


Позиционная строгость и линейное чтение

Парсер Luxon не выполняет семантического анализа структуры строки. Он работает по принципу позиционного соответствия:

  • каждый токен потребляет строго определённое количество символов;
  • последующие токены начинают разбор сразу после предыдущих;
  • любые лишние или недостающие символы приводят к сбою.

Пример:

DateTime.fromFormat("2026-01-25T14:30", "yyyy-MM-dd HH:mm");

Несоответствие возникает из-за символа T, который не описан в формате.

Для корректного разбора:

DateTime.fromFormat("2026-01-25T14:30", "yyyy-MM-dd'T'HH:mm");

Литералы и экранирование

Любые символы, не являющиеся токенами, трактуются как литералы только при явном указании.

Литеральный текст

DateTime.fromFormat("2026 год 25-01", "yyyy 'год' dd-MM");

Ключевой принцип: всё внутри одинарных кавычек не интерпретируется как токены.


Специальные символы

Некоторые символы требуют обязательного экранирования:

  • пробелы могут быть как литералы
  • двоеточие :
  • тире -
  • буквы вне токенов

Токены времени и их согласованность

Часы

  • H — 0–23
  • HH — 00–23
  • h — 1–12 (12-часовой формат)
  • hh — 01–12

При использовании 12-часового формата обязательным становится токен AM/PM:

DateTime.fromFormat("03:30 PM", "hh:mm a");

Токен a определяет период суток:

  • AM
  • PM

Несоответствие регистра или отсутствия токена приводит к ошибке парсинга.


Минуты и секунды

  • m, mm — минуты
  • s, ss — секунды

Эти токены не допускают дробных значений и строго интерпретируют числовую часть строки.


Токены часового пояса

Работа с временными зонами требует отдельного набора токенов:

  • Z — смещение от UTC в формате ±HH:mm
  • ZZ — расширенный формат
  • z — название зоны (ограниченная поддержка)

Пример:

DateTime.fromFormat("2026-01-25 14:30 +05:00", "yyyy-MM-dd HH:mm Z");

Сопоставление смещения требует точного совпадения структуры:

  • знак + или - обязателен;
  • часы и минуты фиксированной длины.

Поведение при несоответствии токенов

При парсинге Luxon не пытается «угадать» значение. Несоответствие приводит к созданию невалидного объекта DateTime.

const dt = DateTime.fromFormat("2026/01/25", "yyyy-MM-dd");

dt.isValid; // false

Основные причины невалидности:

  • несовпадение разделителей;
  • неправильная длина чисел;
  • отсутствие обязательных токенов;
  • лишние символы в строке;
  • несоответствие часового формата.

Строгость режима парсинга

Luxon использует строгую модель соответствия без автоматической нормализации входных данных.

Отсутствуют автоматические преобразования:

  • "1" не становится "01" при MM;
  • "2pm" не распознаётся как валидное время без a;
  • "2026-1-5" не интерпретируется как yyyy-MM-dd.

Эта строгость делает токенизацию предсказуемой и исключает неоднозначные разборы.


Приоритет токенов и конфликтные конструкции

При наличии пересекающихся токенов приоритет определяется порядком следования в формате.

Пример:

DateTime.fromFormat("20260125", "yyyyMMdd");

Разбор выполняется последовательно:

  • yyyy → 2026
  • MM → 01
  • dd → 25

Если структура строки не позволяет однозначно выделить сегменты, парсер не выполняет перераспределение символов.


Повторяющиеся токены

Некоторые токены допускают повторение, но их семантика не меняется:

DateTime.fromFormat("2026 2026 01 25", "yyyy yyyy MM dd");

Второй yyyy интерпретируется независимо, без проверки логической целостности года.


Локализация и текстовые токены

Часть токенов зависит от локали:

  • названия месяцев;
  • дни недели;
  • AM/PM;
  • форматы порядка даты.

Пример:

DateTime.fromFormat("25 janvier 2026", "dd LLLL yyyy", { locale: "fr" });

Токен LLLL соответствует полному названию месяца в выбранной локали. При несоответствии локали разбор не выполняется.


Разбор неоднозначных форматов

Строки с неоднозначной структурой требуют точного формата, иначе результат становится неопределённым.

Пример неоднозначности:

01-02-03

Без формата невозможно определить:

  • год/месяц/день;
  • порядок компонентов.

Токенизация устраняет неоднозначность через явное сопоставление:

DateTime.fromFormat("01-02-03", "yy-MM-dd");

Влияние пробелов и разделителей

Любые пробелы считаются значимыми символами, если они присутствуют в формате.

DateTime.fromFormat("2026-01-25 14:30", "yyyy-MM-dd HH:mm");

При изменении строки:

DateTime.fromFormat("2026-01-25T14:30", "yyyy-MM-dd HH:mm");

результат становится невалидным из-за различия разделителя.


Частично совпадающие строки

Luxon не допускает частичного успешного парсинга. Строка должна быть полностью объяснена форматными токенами.

DateTime.fromFormat("2026-01-25 extra", "yyyy-MM-dd");

остаток extra приводит к ошибке, даже если первые токены совпали корректно.


Контроль соответствия через explain-режим

Для анализа сопоставления токенов используется объясняющий режим:

DateTime.fromFormatExplain("2026-01-25", "yyyy-MM-dd");

Он позволяет определить:

  • какие токены совпали;
  • на каком символе произошёл сбой;
  • как интерпретированы части строки.

Итоговая модель сопоставления

Механизм разбора можно представить как последовательную детерминированную машину:

  • формат разбивается на токены;
  • входная строка читается линейно;
  • каждый токен потребляет строго определённый сегмент;
  • любое отклонение нарушает цепочку;
  • результат либо полностью валиден, либо полностью недействителен.

Такое поведение обеспечивает предсказуемость, но требует точного соответствия между форматом и входными данными без допуска к интерпретации или эвристике.