При импорте данных из сторонних систем, текстовых файлов или документов довольно часто встречается ситуация, когда дата передается не в стандартном числовом формате ДД.ММ.ГГГГ, а в виде текстовой строки с наименованием месяца — например, «31 марта 2018 г.» или «1 января 2023». Попытка напрямую передать такую строку в функцию Дата() приведет к ошибке выполнения программы, так как платформа 1С ожидают либо канонический вид строки ГГГГММДДЧЧММСС, либо числовые параметры года, месяца и дня.
В этой статье мы подробно проанализируем различные подходы к решению данной задачи, разберем готовые примеры кода и рассмотрим частые подводные камни, с которыми сталкиваются разработчики при парсинге строковых дат.
Платформа 1С:Предприятие умеет автоматически преобразовывать в тип Дата строки строго определенного формата (например, "20180331000000" или "20180331"). Если же в строке присутствуют текстовые названия месяцев («марта», «апреля» и т. д.) или дополнительные символы («г.», пробелы), системная функция преобразования не сможет распознать дату.
Следовательно, наша задача сводится к следующим шагам:
Дата() или конструктора Дата(Год, Месяц, День).Рассмотрим наиболе простой и наглядный подход, если входная строка имеет фиксированный формат. Нам достаточно последовательно заменить название каждого месяца на его числовой аналог, обрамленный точками, и привести полученную строку к нужному формату.
Посмотрим на пример реализации данного алгоритма:
ИсходнаяСтрока = "31 марта 2018 г.";
// Выполняем последовательную замену названий месяцев
СтрДата = СтрЗаменить(НРег(ИсходнаяСтрока), " января ", ".1.");
СтрДата = СтрЗаменить(СтрДата, " февраля ", ".2.");
СтрДата = СтрЗаменить(СтрДата, " марта ", ".3.");
СтрДата = СтрЗаменить(СтрДата, " апреля ", ".4.");
СтрДата = СтрЗаменить(СтрДата, " мая ", ".5.");
СтрДата = СтрЗаменить(СтрДата, " июня ", ".6.");
СтрДата = СтрЗаменить(СтрДата, " июля ", ".7.");
СтрДата = СтрЗаменить(СтрДата, " августа ", ".8.");
СтрДата = СтрЗаменить(СтрДата, " сентября ", ".9.");
СтрДата = СтрЗаменить(СтрДата, " октября ", ".10.");
СтрДата = СтрЗаменить(СтрДата, " ноября ", ".11.");
СтрДата = СтрЗаменить(СтрДата, " декабря ", ".12.");
// Удаляем хвост "г." и добавляем время, чтобы функция Дата() корректно распознала строку
СтрДата = СтрЗаменить(СтрДата, "г.", "");
СтрДата = СокрЛП(СтрДата) + " 00:00:00";
// Преобразуем итоговую строку в дату
ИтоговаяДата = Дата(СтрДата);
Обратите внимание: мы используем НРег() для приведения всей строки к нижнему регистру, чтобы избежать ошибок из-за написания месяца с прописной буквы.
Если формат строки может варьироваться (например, день или год содержат разное количество пробелов или отсутствует буква «г.»), более надежным решением будет разбор строки на составляющие с помощью списка значений или массива.
Проанализируем универсальный алгоритм, который ищет наименование месяца и извлекает цифры дня и года:
СтрокаДаты = "1 марта 2018 г.";
// Создаем и заполняем список месяцев в родительном падеже
СписокМесяцев = Новый СписокЗначений;
СписокМесяцев.Добавить("ЯНВАРЯ");
СписокМесяцев.Добавить("ФЕВРАЛЯ");
СписокМесяцев.Добавить("МАРТА");
СписокМесяцев.Добавить("АПРЕЛЯ");
СписокМесяцев.Добавить("МАЯ");
СписокМесяцев.Добавить("ИЮНЯ");
СписокМесяцев.Добавить("ИЮЛЯ");
СписокМесяцев.Добавить("АВГУСТА");
СписокМесяцев.Добавить("СЕНТЯБРЯ");
СписокМесяцев.Добавить("ОКТЯБРЯ");
СписокМесяцев.Добавить("НОЯБРЯ");
СписокМесяцев.Добавить("ДЕКАБРЯ");
ПроверяемаяСтрока = ВРег(СтрокаДаты);
НомерМесяца = 0;
// Определяем номер месяца
Для Индекс = 0 По СписокМесяцев.Количество() - 1 Цикл
Если Найти(ПроверяемаяСтрока, СписокМесяцев[Индекс].Значение) > 0 Тогда
НомерМесяца = Индекс + 1;
Прервать;
КонецЕсли;
КонецЦикла;
Если НомерМесяца = 0 Тогда
ВызватьИсключение "Не удалось определить месяц в строке: " + СтрокаДаты;
КонецЕсли;
// Извлекаем день и год с помощью анализа цифровых символов
Цифры = "0123456789";
ДеньСтр = "";
ГодСтр = "";
ПервоеЧислоНайдено = Ложь;
Для Позиция = 1 По СтрДлина(СтрокаДаты) Цикл
Символ = Сред(СтрокаДаты, Позиция, 1);
Если Найти(Цифры, Символ) > 0 Тогда
Если НЕ ПервоеЧислоНайдено Тогда
ДеньСтр = ДеньСтр + Символ;
Иначе
ГодСтр = ГодСтр + Символ;
Если СтрДлина(ГодСтр) = 4 Тогда
Прервать; // Год сформирован
КонецЕсли;
КонецЕсли;
Иначе
Если ЗначениеЗаполнено(ДеньСтр) Тогда
ПервоеЧислоНайдено = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Формируем дату через числовой конструктор
ИтоговаяДата = Дата(Число(ГодСтр), НомерМесяца, Число(ДеньСтр));
Сообщить("Преобразованная дата: " + ИтоговаяДата);
Для ускорения работы и поддержки различных падежей (например, если встречаются варианты «март» и «марта») удобно использовать объект Соответствие (словари). В него можно занести все возможные формы названий месяцев.
Рассмотрим, как это реализовать на практике:
Функция ПреобразоватьТекстВДату(Знач ВходнаяСтрока) Экспорт
// Заменяем неразрывные пробелы на обычные
ВходнаяСтрока = СтрЗаменить(ВходнаяСтрока, Символы.НПП, " ");
ВходнаяСтрока = НРег(СокрЛП(ВходнаяСтрока));
// Подготовка словаря месяцев
СловарьМесяцев = Новый Соответствие;
// Январь
СловарьМесяцев.Вставить("января", 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";
КонецФункции
Очень часто задача преобразования текстовой даты возникает при чтении файла MS Excel через COM-объект. Программисты пишут следующий код:
// Ошибка подстерегает здесь!
СтрокаДаты = ЯчейкаExcel.Text; // Возвращает Отображаемый текст "31 марта 2018 г."
В этом случае Excel отдаёт отформатированный текст ячейки, из-за чего приходится писать сложные алгоритмы парсинга. Однако если в самой ячейке Excel действительно хранится дата, то достаточно считывать свойство Value вместо Text:
// Правильное решение при работе с Excel:
ЗначениеДата = ЯчейкаExcel.Value; // Сразу возвращает значение типа Дата
Использование свойства Value полностью избавляет от необходимости парсить текстовые строки и писать дополнительные функции замены месяцев.
Символы.НПП). Всегда заменяйте Символы.НПП на обычный пробел перед парсингом.Попытка ... Исключение или метод ПривестиЗначение() объекта ОписаниеТипов.Мы проанализировали основные методы преобразования текстовой строки с наименованием месяца в тип Дата. Выбор конкретного способа зависит от источника данных: для фиксированных форматов подойдет быстрая замена через СтрЗаменить, для произвольных строк — разбор с помощью словаря Соответствие, а при импорте из Excel достаточно использовать свойство Value.