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

Примеры входящих данных:

  • в понедельник, 14:33
  • две минуты назад
  • вчера, 18:23
  • сегодня, 19:47
  • 2018-11-17 18:22

Предварительный план Link to heading

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

Шаги Link to heading

  1. Преобразовать в английский язык
  2. Очистить от шума
    • не переведенные части
    • симводы не нужные для понимания временной метки
  3. Применение существующих решений для анализа строки средствами php
    • strtotime + свой преобразователь в английский
    • Intl
    • Carbon
  4. Выбор наиболее оптимального результата

Начальная информация Link to heading

Пример входящей информации для анализа.

Локаль: ru_RU

Временная зона: Asia/Magadan

Входящие данные: понедельник, 14:30

Дата анализа данных: 2018-11-21 12:00

Дата сбора данных: 2018-11-14 14:00

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

Дата для будущего времени: 2018-11-19 14:30

Дата для прошлого времени: 2018-11-12 14:30

Свое решение 🚲 Link to heading

Описать все возомжные варианты слов связаных с датой с последующим приведением к понимаемому формату для парсера strtotime.

Пример регулярного выражения для дня недели:

~(?<=\W|^)(?<weekday>(сб\.?|суб(\.|бот(а|у|ой|е)?)?))(?=\W|$)~imu

Далее заменим на английское слово, очистим от возможных шумов и отправим на обработку strtotime

<?php
// Исходное значение
$ruDateTimeString = 'понедельник, 14:30';
// Инициализируем парсер
$parser = new MySuperDateTimeParser('ru_RU');
// Дата когда мы увидели сообщение о относительно дате (учтите временную зону)
$collectedAt = strtotime('2018-11-14 14:00');
// Превращаем в strtotime формат
$enDateTimeString = $parser->parse($ruDateTimeString); // 'monday, 14:30'

$documentTimestamp = strtotime($enDateTimeString, $collectedAt); // 2018-11-19 14:30

Успех, осталось настроить аналогичные регулярки для остальных примеров и всех их вариаций, и в теории будет работать (сарказм). Такой подход хорош, когда в этом языке есть эксперные знания, и вы знаете все слонения и прочие сокращения которые могут употребляться. А что делать для другого языка, такого как Голандский на пример? Я голандский не знаю, по этому я делать буду ни-че-го.

Intl Link to heading

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

Для применения этого инструмента, достаточно открыть документацию о классе IntlDateFormatter

<?php
$df = IntlDateFormatter::create(
    'ru_RU', 
    IntlDateFormatter::FULL, 
    IntlDateFormatter::FULL, 
    'Asia/Magadan',
    IntlDateFormatter::GREGORIAN
);

echo date('Y-m-d', $df->parse('понедельник, 14:30'));

// Далее действия с интервалом для вычисления даты относительно даты анализа данных

Вернемся к Голандскому, его эта библиотека скушает с удовольствием

<?php
$df = IntlDateFormatter::create(
    'nl_NL', 
    IntlDateFormatter::FULL, 
    IntlDateFormatter::FULL, 
    'Europe/Amsterdam',
    IntlDateFormatter::GREGORIAN
);

echo date('Y-m-d', $df->parse('maandag, 14:30'));

// Далее действия с интервалом для вычисления даты относительно даты анализа данных

Carbon 2 Link to heading

Тоже очень хорошо развитая библиотека написаная php, все языковые конструкции описаны в массивах, имеет большой набор правил на разных языках. Эта библиотека расширяет функционал DateTime класса.

Поведение похоже на Intl:

<?php
\Carbon\Carbon::setLocale('ru_RU');

$carbon = new \Carbon\Carbon('понедельник, 14:30');

// Далее действия с интервалом для вычисления даты относительно даты анализа данных

Прочее Link to heading

Если не брать только php, то есть хорошая библиотека на python от ребят из scrapinghub по имени dateparser.

P.S. Её я и возьму.