При интеграции 1С с внешними системами, парсинге файлов или обработке текстовых данных разработчики часто сталкиваются со строками, содержащими дату в нестандартном или «замусоренном» виде. Типичный пример такой строки — "28.02.2019 (Чт)", где помимо самой даты присутствует текстовое сокращение дня недели в скобках. Платформа 1С:Предприятие не умеет напрямую преобразовывать такие строки в тип Дата через стандартный конструктор, что приводит к возникновению ошибок времени выполнения.
В этой статье мы подробно и по шагам разберем, как правильно, быстро и безопасно решить эту задачу. Мы рассмотрим как простые классические методы манипуляции со строками, так и более профессиональные подходы с использованием Библиотеки стандартных подсистем (БСП) и защитой от региональных настроек операционной системы.
Это классический и самый очевидный способ решения задачи «в лоб», когда мы точно уверены в неизменности формата входящей строки. Мы знаем, что первые 10 символов строки всегда содержат дату в формате ДД.ММ.ГГГГ, а все последующие символы (день недели в скобках) являются избыточными для нашей задачи.
Для того чтобы преобразовать такую строку в системную дату, нам необходимо привести ее к каноническому формату 1С: ГГГГММДДХХММСС (например, "20190228000000"). Давайте разберем код, который решает эту задачу с помощью функций Лев() и Сред():
СтрокаДата = "28.02.2019 (Чт)";
// Выделяем составляющие даты по фиксированным позициям
ГодСтрокой = Сред(СтрокаДата, 7, 4); // Извлекаем "2019"
МесяцСтрокой = Сред(СтрокаДата, 4, 2); // Извлекаем "02"
ДеньСтрокой = Лев(СтрокаДата, 2); // Извлекаем "28"
// Собираем каноническую строку формата YYYYMMDD
КаноническаяСтрока = ГодСтрокой + МесяцСтрокой + ДеньСтрокой;
// Преобразуем полученную строку в полноценную дату
Результат = Дата(КаноническаяСтрока);
Преимущества метода: Высокая скорость работы на уровне платформы, отсутствие зависимости от сторонних библиотек.
Недостатки метода: Полная зависимость от жесткого формата строки. Если количество знаков в дне или месяце изменится (например, придет строка вида "8.2.2019 (Чт)" вместо "08.02.2019"), алгоритм вернет неверный результат или вызовет ошибку.
Если разделители внутри даты (точки) гарантированно присутствуют, но длина сегментов теоретически может меняться, более гибким решением будет использование функции СтрРазделить(). Мы можем разбить строку по символу точки, а затем отбросить лишний «хвост» с днем недели.
Проанализируем пример реализации этого подхода:
СтрокаДата = "28.02.2019 (Чт)";
// Разбиваем строку на части, используя в качестве разделителя точку
МассивЧастей = СтрРазделить(СтрокаДата, ".");
// В массиве у нас получатся следующие элементы:
// МассивЧастей[0] = "28"
// МассивЧастей[1] = "02"
// МассивЧастей[2] = "2019 (Чт)"
// Теперь нам нужно очистить год от скобок и дня недели.
// Так как год всегда идет первым в третьем сегменте и состоит из 4 символов,
// мы можем безопасно применить функцию Лев().
ГодОчищенный = Лев(СокрЛП(МассивЧастей[2]), 4);
// Собираем дату через стандартный конструктор даты по трем составляющим: Год, Месяц, День
Результат = Дата(ГодОчищенный, МассивЧастей[1], МассивЧастей[0]);
Преимущества метода: Данный код более устойчив к ситуации, когда дни или месяцы записаны без лидирующих нулей (например, "8.2.2019").
Недостатки метода: Требует создания промежуточного объекта типа Массив в памяти, что незначительно снижает общую производительность при пакетной обработке сотен тысяч строк.
Иногда разработчики пытаются применить сокращенный вариант преобразования, просто отрезая скобки и добавляя время по умолчанию:
СтрокаДата = "28.02.2019 (Чт)";
Результат = Дата(Лев(СтрокаДата, 10) + " 00:00:00");
Внимание! Это опасный и крайне нерекомендуемый метод.
Выясним причину опасности. Работа конструкции Дата("ДД.ММ.ГГГГ ЧЧ:ММ:СС") напрямую зависит от региональных настроек операционной системы, на которой выполняется данный код. Если на компьютере пользователя или на сервере приложений 1С установлена русская локаль, код отработает успешно. Однако, если сервер 1С развернут на операционной системе с английскими региональными стандартами (где принят формат даты MM/DD/YYYY), этот код упадет с критической ошибкой «Преобразование значения к типу Дата не может быть выполнено».
Поэтому всегда соблюдайте золотое правило разработки в 1С: для приведения строк к типу Дата используйте только канонический вид "ГГГГММДДХХММСС" или передавайте в конструктор Дата() три или шесть числовых параметров (Год, Месяц, День, Час, Минута, Секунда).
Любая попытка преобразовать некорректную строку в дату напрямую через вызов функции Дата() приводит к аварийной остановке выполнения программы. Чтобы избежать этого и не загромождать код конструкциями Попытка...Исключение, мы можем применить объект ОписаниеТипов.
Давайте посмотрим на пример безопасного парсинга:
СтрокаДата = "28.02.2019 (Чт)";
// Сначала готовим каноническую строку (например, способом №1)
КаноническаяСтрока = Сред(СтрокаДата, 7, 4) + Сред(СтрокаДата, 4, 2) + Лев(СтрокаДата, 2);
// Создаем описание типа "Дата"
ОписаниеДаты = Новый ОписаниеТипов("Дата");
// Метод ПривестиЗначение вернет пустую дату '00010101', если строка повреждена,
// вместо вызова критической ошибки системы.
РабочаяДата = ОписаниеДаты.ПривестиЗначение(КаноническаяСтрока);
Если РабочаяДата = '00010101' Тогда
// Обрабатываем ситуацию, когда формат входных данных нарушен
Сообщить("Не удалось корректно распознать дату из строки!");
КонецЕсли;
Представим ситуацию, когда входящий формат строки нестабилен. В один день внешняя система присылает нам строку "28.02.2019 (Чт)", в другой — "28/02/2019 [Четверг]", а в третий — "28-02-2019 (Thu)".
Чтобы написать по-настоящему надежный и устойчивый к изменениям парсер, мы можем предварительно очистить строку от любых посторонних символов (букв, пробелов, скобок, дефисов), оставив в ней исключительно цифры. Проанализируем этот универсальный алгоритм:
СтрокаДата = "28.02.2019 (Чт)";
ТолькоЦифры = "";
// Проходим циклом по каждому символу строки и отбираем только цифры
Для Индекс = 1 По СтрДлина(СтрокаДата) Цикл
Символ = Сред(СтрокаДата, Индекс, 1);
Если Найти("0123456789", Символ) > 0 Тогда
ТолькоЦифры = ТолькоЦифры + Символ;
КонецЕсли;
КонецЦикла;
// На выходе из цикла в переменной ТолькоЦифры мы гарантированно получим "28022019".
// Теперь, зная, что первые 2 знака - это день, следующие 2 - месяц, а последние 4 - год,
// мы легко собираем каноническую строку формата YYYYMMDD:
КаноническаяСтрока = Прав(ТолькоЦифры, 4) + Сред(ТолькоЦифры, 3, 2) + Лев(ТолькоЦифры, 2);
// Безопасно преобразуем результат в Дату
ДатаРезультат = Дата(КаноническаяСтрока);
Этот подход гарантирует стабильную работу системы обмена данными, даже если поставщик информации решит изменить разделители дат или язык написания дней недели.
Если вы ведете разработку в любой современной типовой конфигурации 1С (такой как 1С:Бухгалтерия, 1С:ERP, 1С:УТ, 1С:УНФ и др.), в вашем распоряжении уже есть мощный готовый инструмент, встроенный в Библиотеку стандартных подсистем (БСП).
Вам не нужно писать собственные циклы разбора строк. Достаточно вызвать готовую экспортную функцию общего модуля:
СтрокаДата = "28.02.2019 (Чт)";
// Функция БСП самостоятельно очистит строку от нерелевантных символов
// и вернет значение типа Дата. Если преобразование невозможно, вернется пустая дата.
ДатаРезультат = СтроковыеФункцииКлиентСервер.СтрокаВДату(СтрокаДата);
Использование функций БСП считается правилом хорошего тона при профессиональной разработке в среде 1С, так как этот код уже оптимизирован разработчиками фирмы «1С» и прошел всестороннее тестирование.
В заключение рассмотрим ситуацию, когда дата приходит к вам в стандартном международном формате (например, "2019-02-28" или "2019-02-28T00:00:00"). Чаще всего это происходит при получении ответов от веб-сервисов, REST API или при чтении файлов формата JSON/XML.
Вместо ручного разбора дефисов и символа "T", используйте встроенную функцию платформы XMLЗначение:
СтрокаISO = "2019-02-28";
// Быстрое преобразование на уровне платформы
ДатаРезультат = XMLЗначение(Тип("Дата"), СтрокаISO);
Этот метод работает чрезвычайно быстро, так как выполняется на уровне скомпилированного С++ кода самой платформы 1С:Предприятие, минуя интерпретатор встроенного языка.
Для правильного выбора метода преобразования строки в дату придерживайтесь следующих рекомендаций:
СтроковыеФункцииКлиентСервер.СтрокаВДату().Сред() и Лев() для сборки строки вида "YYYYMMDD".XMLЗначение.Дата() с разделителями-точками и косыми чертами.