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

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

При импорте данных из сторонних систем, текстовых файлов или документов довольно часто встречается ситуация, когда дата передается не в стандартном числовом формате ДД.ММ.ГГГГ, а в виде текстовой строки с наименованием месяца — например, «31 марта 2018 г.» или «1 января 2023». Попытка напрямую передать такую строку в функцию Дата() приведет к ошибке выполнения программы, так как платформа 1С ожидают либо канонический вид строки ГГГГММДДЧЧММСС, либо числовые параметры года, месяца и дня.

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

Причина возникновения проблемы и базовый принцип решения

Платформа 1С:Предприятие умеет автоматически преобразовывать в тип Дата строки строго определенного формата (например, "20180331000000" или "20180331"). Если же в строке присутствуют текстовые названия месяцев («марта», «апреля» и т. д.) или дополнительные символы («г.», пробелы), системная функция преобразования не сможет распознать дату.

Следовательно, наша задача сводится к следующим шагам:

  1. Очистить входную строку от лишних символов (обозначения года «г.», лишних пробелов).
  2. Заменить текстовое наименование месяца на его порядковый числовой номер (от 1 до 12).
  3. Сформировать итоговую дату с помощью функции Дата() или конструктора Дата(Год, Месяц, День).

Способ 1. Простая замена текстовых месяцев через СтрЗаменить

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

Посмотрим на пример реализации данного алгоритма:


ИсходнаяСтрока = "31 марта 2018 г.";

// Выполняем последовательную замену названий месяцев
СтрДата = СтрЗаменить(НРег(ИсходнаяСтрока), " января ", ".1.");
СтрДата = СтрЗаменить(СтрДата, " февраля ", ".2.");
СтрДата = СтрЗаменить(СтрДата, " марта ", ".3.");
СтрДата = СтрЗаменить(СтрДата, " апреля ", ".4.");
СтрДата = СтрЗаменить(СтрДата, " мая ", ".5.");
СтрДата = СтрЗаменить(СтрДата, " июня ", ".6.");
СтрДата = СтрЗаменить(СтрДата, " июля ", ".7.");
СтрДата = СтрЗаменить(СтрДата, " августа ", ".8.");
СтрДата = СтрЗаменить(СтрДата, " сентября ", ".9.");
СтрДата = СтрЗаменить(СтрДата, " октября ", ".10.");
СтрДата = СтрЗаменить(СтрДата, " ноября ", ".11.");
СтрДата = СтрЗаменить(СтрДата, " декабря ", ".12.");

// Удаляем хвост "г." и добавляем время, чтобы функция Дата() корректно распознала строку
СтрДата = СтрЗаменить(СтрДата, "г.", "");
СтрДата = СокрЛП(СтрДата) + " 00:00:00";

// Преобразуем итоговую строку в дату
ИтоговаяДата = Дата(СтрДата);

Обратите внимание: мы используем НРег() для приведения всей строки к нижнему регистру, чтобы избежать ошибок из-за написания месяца с прописной буквы.

Способ 2. Использование списка значений и разбора строки по частям

Если формат строки может варьироваться (например, день или год содержат разное количество пробелов или отсутствует буква «г.»), более надежным решением будет разбор строки на составляющие с помощью списка значений или массива.

Проанализируем универсальный алгоритм, который ищет наименование месяца и извлекает цифры дня и года:


СтрокаДаты = "1 марта 2018 г.";

// Создаем и заполняем список месяцев в родительном падеже
СписокМесяцев = Новый СписокЗначений;
СписокМесяцев.Добавить("ЯНВАРЯ");
СписокМесяцев.Добавить("ФЕВРАЛЯ");
СписокМесяцев.Добавить("МАРТА");
СписокМесяцев.Добавить("АПРЕЛЯ");
СписокМесяцев.Добавить("МАЯ");
СписокМесяцев.Добавить("ИЮНЯ");
СписокМесяцев.Добавить("ИЮЛЯ");
СписокМесяцев.Добавить("АВГУСТА");
СписокМесяцев.Добавить("СЕНТЯБРЯ");
СписокМесяцев.Добавить("ОКТЯБРЯ");
СписокМесяцев.Добавить("НОЯБРЯ");
СписокМесяцев.Добавить("ДЕКАБРЯ");

ПроверяемаяСтрока = ВРег(СтрокаДаты);
НомерМесяца = 0;

// Определяем номер месяца
Для Индекс = 0 По СписокМесяцев.Количество() - 1 Цикл
    Если Найти(ПроверяемаяСтрока, СписокМесяцев[Индекс].Значение) > 0 Тогда
        НомерМесяца = Индекс + 1;
        Прервать;
    КонецЕсли;
КонецЦикла;

Если НомерМесяца = 0 Тогда
    ВызватьИсключение "Не удалось определить месяц в строке: " + СтрокаДаты;
КонецЕсли;

// Извлекаем день и год с помощью анализа цифровых символов
Цифры = "0123456789";
ДеньСтр = "";
ГодСтр = "";
ПервоеЧислоНайдено = Ложь;

Для Позиция = 1 По СтрДлина(СтрокаДаты) Цикл
    Символ = Сред(СтрокаДаты, Позиция, 1);
    Если Найти(Цифры, Символ) > 0 Тогда
        Если НЕ ПервоеЧислоНайдено Тогда
            ДеньСтр = ДеньСтр + Символ;
        Иначе
            ГодСтр = ГодСтр + Символ;
            Если СтрДлина(ГодСтр) = 4 Тогда
                Прервать; // Год сформирован
            КонецЕсли;
        КонецЕсли;
    Иначе
        Если ЗначениеЗаполнено(ДеньСтр) Тогда
            ПервоеЧислоНайдено = Истина;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

// Формируем дату через числовой конструктор
ИтоговаяДата = Дата(Число(ГодСтр), НомерМесяца, Число(ДеньСтр));
Сообщить("Преобразованная дата: " + ИтоговаяДата);

Способ 3. Оптимизированный разбор с использованием Соответствия

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

Рассмотрим, как это реализовать на практике:


Функция ПреобразоватьТекстВДату(Знач ВходнаяСтрока) Экспорт

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

    // Разбиваем строку по пробелам
    МассивСлов = СтрРазделить(ВходнаяСтрока, " ", Ложь);
    
    ДеньЧисло = 0;
    МесяцЧисло = 0;
    ГодЧисло = 0;
    
    Для Каждого Слово Из МассивСлов Цикл
        СловоОчищенное = СтрЗаменить(Слово, ".", "");
        
        // Проверяем, является ли слово месяцем
        НайденныйМесяц = СловарьМесяцев.Получить(СловоОчищенное);
        Если НайденныйМесяц <> Неопределено Тогда
            МесяцЧисло = НайденныйМесяц;
            Продолжить;
        КонецЕсли;
        
        // Если это число, определяем день или год
        Если ТолькоЦифры(СловоОчищенное) Тогда
            ЗначениеЧисла = Число(СловоОчищенное);
            Если ЗначениеЧисла > 31 Тогда
                ГодЧисло = ЗначениеЧисла;
            ИначеЕсли ДеньЧисло = 0 Тогда
                ДеньЧисло = ЗначениеЧисла;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;

    Если ДеньЧисло > 0 И МесяцЧисло > 0 И ГодЧисло > 0 Тогда
        Возврат Дата(ГодЧисло, МесяцЧисло, ДеньЧисло);
    КонецЕсли;

    Возврат Дата(1, 1, 1); // Возвращаем пустую дату в случае ошибки
    
КонецФункции

Вспомогательная Функция ТолькоЦифры(Строка)
    ОписаниеТипаЧисло = Новый ОписаниеТипов("Число");
    Возврат ОписаниеТипаЧисло.ПривестиЗначение(Строка) <> 0 ИЛИ Строка = "0";
КонецФункции

Важный подводный камень: загрузка данных из Excel

Очень часто задача преобразования текстовой даты возникает при чтении файла MS Excel через COM-объект. Программисты пишут следующий код:


// Ошибка подстерегает здесь!
СтрокаДаты = ЯчейкаExcel.Text; // Возвращает Отображаемый текст "31 марта 2018 г."

В этом случае Excel отдаёт отформатированный текст ячейки, из-за чего приходится писать сложные алгоритмы парсинга. Однако если в самой ячейке Excel действительно хранится дата, то достаточно считывать свойство Value вместо Text:


// Правильное решение при работе с Excel:
ЗначениеДата = ЯчейкаExcel.Value; // Сразу возвращает значение типа Дата

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

Полезные советы и нюансы

  1. Неразрывные пробелы: При копировании текста с сайтов или загрузке из HTML/XML между днем, месяцем и годом часто стоит не обычный пробел (ASCII 32), а неразрывный пробел (ASCII 160 или Символы.НПП). Всегда заменяйте Символы.НПП на обычный пробел перед парсингом.
  2. Проверка некорректных дат: Для предотвращения падения программы при передаче невалидных строк (например, «31 февраля») используйте блок Попытка ... Исключение или метод ПривестиЗначение() объекта ОписаниеТипов.

Мы проанализировали основные методы преобразования текстовой строки с наименованием месяца в тип Дата. Выбор конкретного способа зависит от источника данных: для фиксированных форматов подойдет быстрая замена через СтрЗаменить, для произвольных строк — разбор с помощью словаря Соответствие, а при импорте из Excel достаточно использовать свойство Value.

← На главную