Как получить дату из строки с текстовым месяцем в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции с внешними системами, импорте документов из таблиц Excel или разборе текстовых выгрузок часто приходится сталкиваться со строковым представлением даты в развернутом виде, например: "09 августа 2017 г.". В платформе 1С:Предприятие отсутствует прямая обратная функция к Формат(), способная автоматически разобрать произвольный текст с названиями месяцев на естественном языке. Конструктор Дата() от строки принимает только канонический формат ("ГГГГММДД" или "ГГГГММДДЧЧММСС"), либо требует передачи числовых параметров года, месяца и дня.

Рассмотрим, как правильно решить задачу преобразования такой строки в стандартный тип Дата, разберем возможные способы и обратим внимание на важные нюансы обработки текста.

Способ 1. Использование объекта Соответствие для сопоставления месяцев

Наиболее надежный, понятный и быстродействующий способ — разобрать строку на составные части (день, месяц, год) и получить порядковый номер месяца с помощью объекта Соответствие. Это исключает путаницу между похожими названиями (например, «Май» и «Март»).

Разберем по шагам реализацию универсальной функции:

  1. Очищаем строку от лишних символов и неразрывных пробелов.
  2. Разбиваем строку на массив подстрок по пробелам.
  3. Извлекаем номер месяца из подготовленного словаря соответствий.
  4. Формируем результирующую дату с помощью конструктора Дата(Год, Месяц, День).

Посмотрим на пример функции:


Функция ДатаИзСтрокиСМесяцем(Знач ИсходнаяСтрока) Экспорт
    
    // Заменяем неразрывные пробелы на обычные и удаляем пробелы по краям
    СтрокаОчищенная = СокрЛП(СтрЗаменить(ИсходнаяСтрока, Символы.НПП, " "));
    
    // Разбиваем строку на части по пробелу
    ЧастиСтроки = СтрРазделить(СтрокаОчищенная, " ", Ложь);
    
    Если ЧастиСтроки.Количество() < 3 Тогда
        Возврат Дата(1, 1, 1);
    КонецЕсли;
    
    ДеньСтрокой = ЧастиСтроки[0];
    МесяцСтрокой = НРег(ЧастиСтроки[1]);
    ГодСтрокой = ЧастиСтроки[2];
    
    // Формируем таблицу сопоставления названий месяцев
    КартаМесяцев = Новый Соответствие;
    КартаМесяцев.Вставить("января", 1);
    КартаМесяцев.Вставить("февраля", 2);
    КартаМесяцев.Вставить("марта", 3);
    КартаМесяцев.Вставить("апреля", 4);
    КартаМесяцев.Вставить("мая", 5);
    КартаМесяцев.Вставить("июня", 6);
    КартаМесяцев.Вставить("июля", 7);
    КартаМесяцев.Вставить("августа", 8);
    КартаМесяцев.Вставить("сентября", 9);
    КартаМесяцев.Вставить("октября", 10);
    КартаМесяцев.Вставить("ноября", 11);
    КартаМесяцев.Вставить("декабря", 12);
    
    // Поддержка именительного падежа
    КартаМесяцев.Вставить("январь", 1);
    КартаМесяцев.Вставить("февраль", 2);
    КартаМесяцев.Вставить("март", 3);
    КартаМесяцев.Вставить("апрель", 4);
    КартаМесяцев.Вставить("май", 5);
    КартаМесяцев.Вставить("июнь", 6);
    КартаМесяцев.Вставить("июль", 7);
    КартаМесяцев.Вставить("август", 8);
    КартаМесяцев.Вставить("сентябрь", 9);
    КартаМесяцев.Вставить("октябрь", 10);
    КартаМесяцев.Вставить("ноябрь", 11);
    КартаМесяцев.Вставить("декабрь", 12);
    
    НомерМесяца = КартаМесяцев[МесяцСтрокой];
    Если НомерМесяца = Неопределено Тогда
        Возврат Дата(1, 1, 1);
    КонецЕсли;
    
    ДеньЧислом = Число(ДеньСтрокой);
    ГодЧислом  = Число(ГодСтрокой);
    
    Попытка
        Возврат Дата(ГодЧислом, НомерМесяца, ДеньЧислом);
    Исключение
        Возврат Дата(1, 1, 1);
    КонецПопытки;
    
КонецФункции

Способ 2. Поиск по списку или массиву подстрок

Рассмотрим альтернативный вариант, когда для определения месяца используется массив базовых основ названий. При таком подходе важно учитывать коллизии коротких слов: если искать основу "Ма", условие сработает и для месяца «Март», поэтому проверять или хранить основы нужно внимательно либо искать полное совпадение слова.

Посмотрим на пример с массивом основ и приведением к канонической строке "ГГГГММДД":


Функция ПолучитьНомерМесяцаПоМассиву(Знач НазваниеМесяца)
    
    НазваниеМесяца = НРег(СокрЛП(НазваниеМесяца));
    
    МассивМесяцев = Новый Массив;
    МассивМесяцев.Добавить("январ");
    МассивМесяцев.Добавить("феврал");
    МассивМесяцев.Добавить("март");
    МассивМесяцев.Добавить("апрел");
    МассивМесяцев.Добавить("май");
    МассивМесяцев.Добавить("июн");
    МассивМесяцев.Добавить("июл");
    МассивМесяцев.Добавить("август");
    МассивМесяцев.Добавить("сентябр");
    МассивМесяцев.Добавить("октябр");
    МассивМесяцев.Добавить("ноябр");
    МассивМесяцев.Добавить("декабр");
    
    // Специальная обработка для родительного падежа месяца "май" -> "мая"
    Если НазваниеМесяца = "мая" Тогда
        Возврат "05";
    КонецЕсли;
    
    Для Индекс = 0 По МассивМесяцев.ВГраница() Цикл
        Если СтрНайти(НазваниеМесяца, МассивМесяцев[Индекс]) > 0 Тогда
            Возврат Формат(Индекс + 1, "ЧЦ=2; ЧВН=");
        КонецЕсли;
    КонецЦикла;
    
    Возврат "01";
    
КонецФункции

Способ 3. Автоматическое заполнение карты месяцев через форматирование

Чтобы не заполнять вручную список из 12 или 24 вариантов названий месяцев, проанализируем возможность платформы генерировать названия программно. В русской локализации форматная строка "ДФ='MMMM'" возвращает название в именительном падеже, а "ДЛФ=DD" — длинную дату со склонением месяца в родительном падеже.

Разберем пример генерации соответствия месяцев на лету:


Функция ПолучитьКартуМесяцевАвто()
    
    Карта = Новый Соответствие;
    БазоваяДата = Дата(2020, 1, 1); // 2020 год для корректности високосного февраля
    
    Для Номер = 1 По 12 Цикл
        ТекущаяДата = ДобавитьМесяц(БазоваяДата, Номер - 1);
        
        // Получаем именительный падеж ("январь", "февраль"...)
        ИмяИменительный = НРег(Формат(ТекущаяДата, "ДФ='MMMM'"));
        Карта.Вставить(ИмяИменительный, Номер);
        
        // Получаем родительный падеж ("января", "февраля"...)
        СтрокаДлиннойДаты = Формат(ТекущаяДата, "ДЛФ=DD"); // "01 января 2020 г."
        СловаДаты = СтрРазделить(СтрокаДлиннойДаты, " ");
        Если СловаДаты.Количество() >= 2 Тогда
            ИмяРодительный = НРег(СловаДаты[1]);
            Карта.Вставить(ИмяРодительный, Номер);
        КонецЕсли;
    КонецЦикла;
    
    Возврат Карта;
    
КонецФункции

Подводные камни и частые ошибки при разборе строковых дат

Выясним основные причины, из-за которых алгоритмы разбора могут давать сбой:

  1. Неразрывные пробелы: Текст, выгруженный из таблиц Excel, веб-документов или отчетов, часто содержит не обычный пробел с кодом 32, а неразрывный пробел Символы.НПП (код 160). Метод СтрРазделить(Строка, " ") не разделит слова по неразрывному пробелу. Перед разбором всегда заменяйте СтрЗаменить(Строка, Символы.НПП, " ").
  2. Регистр символов: В строке месяц может начинаться с заглавной или строчной буквы («Августа», «августа»). Обязательно приводите проверяемое слово к нижнему регистру функцией НРег().
  3. Склонение слов «Май» и «Март»: Основа слова «Май» в родительном падеже превращается в «мая». Если выполнять поиск подстроки по неполному корню "ма", месяц «Март» может ошибочно определиться как «Май». Рекомендуется использовать точное сопоставление целого слова через Соответствие.
  4. Суффиксы года: На конце строки часто присутствуют обозначения "г.", "г" или "года". Если разбор идет по позициям слов через СтрРазделить(), третья часть гарантированно содержит цифры года. Однако при наличии лишних пробелов перед суффиксом числовые значения года следует явно очищать с помощью функции Число().
← На главную