При выгрузке отчетов и печатных форм из 1С в формат Microsoft Excel (xlsx) разработчики часто сталкиваются с неприятной особенностью: ячейки с датами переносятся в итоговый файл как обычные текстовые строки. При попытке выполнить фильтрацию, сортировку или математические расчеты в Excel такие данные ведут себя некорректно. Пользователям приходится вручную преобразовывать формат ячеек, что вызывает массу жалоб.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем внутренние механизмы хранения дат в 1С и Excel, а также рассмотрим несколько эффективных способов решения задачи — от простых манипуляций со свойствами ячеек в 1С до продвинутых серверных методов без использования COM-объектов.
Прежде чем переходить к коду, давайте разберем физическую разницу между хранением дат в обеих системах:
Из-за этих различий при стандартном вызове метода ТабличныйДокумент.Записать() платформа 1С пытается перестраховаться и выгружает дату в виде отформатированной строки, чтобы избежать системных ошибок округления или часовых поясов. В итоге Excel воспринимает ячейку исключительно как текстовое поле.
Рассмотрим по шагам все возможные способы решения этой проблемы.
Самый простой способ, который работает непосредственно в коде 1С без привлечения сторонних инструментов — это правильная комбинация свойств ячейки табличного документа. Основная ошибка разработчиков заключается в одновременном использовании свойств СодержитЗначение и Формат.
Выясним важное правило: если у ячейки установлено свойство СодержитЗначение = Истина, и тип значения определен как Дата, платформа принудительно преобразует ее в текст при экспорте в Excel.
Чтобы дата перенеслась корректно, выполним следующую программную настройку ячеек:
// Предположим, у нас есть ОбластьМакета, в которой находится параметр ДатаДокумента
ОбластьМакета = Макет.ПолучитьОбласть("Строка");
ТекущаяЯчейка = ОбластьМакета.Параметры.ДатаДокумента; // или обращение через Область()
// Настраиваем свойства ячейки табличного документа
Ячейка = ОбластьМакета.Область("R1C2"); // Обращаемся к конкретной ячейке области
Ячейка.СодержитЗначение = Ложь; // Важно! Отключаем признак хранения типизированного значения
Ячейка.Формат = "ДФ=dd.MM.yyyy"; // Задаем маску отображения даты
Ячейка.Значение = ТекущаяДата(); // Записываем стандартную дату 1С
При такой комбинации параметров платформа транслирует формат маски ДФ=dd.MM.yyyy в соответствующий XML-код формата даты в Excel, и ячейка распознается офисным пакетом правильно.
Если стандартный механизм форматирования платформы все равно дает сбои (что иногда случается на старых версиях платформы или в специфических конфигурациях на Linux-серверах), мы можем применить математический подход. Переведем дату в числовой эквивалент, который Excel гарантированно поймет.
Для этого нам нужно рассчитать разницу в днях между нашей датой и базовой датой Excel (01.01.1900). При этом важно учесть известную историческую ошибку Excel (баг совместимости с Lotus 1-2-3), из-за которой 1900 год ошибочно считается високосным. Это сдвигает все расчеты на один день для дат, начиная с 1 марта 1900 года.
Напишем функцию конвертации даты 1С в число Excel:
Функция ПреобразоватьДатуВЧислоExcel(ВходнаяДата) Экспорт
Если Не ЗначениеЗаполнено(ВходнаяДата) Тогда
Возврат 0;
КонецЕсли;
НачалоЭпохиExcel = Дата(1900, 1, 1);
СекундВСутках = 86400;
// Разница в секундах между датами
РазницаВСекундах = ВходнаяДата - НачалоЭпохиExcel;
КоличествоДней = РазницаВСекундах / СекундВСутках;
// Учитываем баг Excel с невисокосным 1900 годом для всех дат после 28.02.1900
Если ВходнаяДата > Дата(1900, 2, 28) Тогда
КоличествоДней = КоличествоДней + 2;
Иначе
КоличествоДней = КоличествоДней + 1;
EndIf;
Возврат Цел(КоличествоДней);
КонецФункции
Теперь применим эту функцию при заполнении табличного документа:
ЧисловоеЗначениеДаты = ПреобразоватьДатуВЧислоExcel(ВыгружаемаяДата);
Ячейка = ОбластьМакета.Область("R1C2");
Ячейка.СодержитЗначение = Истина;
Ячейка.ТипЗначения = Новый ОписаниеТипов("Число");
Ячейка.Значение = ЧисловоеЗначениеДаты;
Ячейка.Формат = "ЧЦ=10; ЧДЦ=0; ЧГ=0"; // Выводим как обычное целое число
После выгрузки такого файла в Excel пользователь увидит число (например, 45291). Чтобы превратить его в дату, достаточно будет выделить колонку и изменить ее формат на «Дата». Плюс данного метода в том, что данные гарантированно не превратятся в нередактируемый текст.
Рассмотрим самый прогрессивный и технологичный вариант, который часто применяется в крупных энтерпрайз-системах. Файл формата .xlsx — это обычный ZIP-архив, содержащий набор XML-документов. Мы можем программно распаковать его прямо на сервере (даже под Linux без установленного Excel), изменить XML-описание стилей и упаковать обратно.
Проанализируем алгоритм этого метода:
ТабличныйДокумент.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX).ЧтениеZipФайла для распаковки полученного файла во временную директорию.xl/worksheets/sheet1.xml) и файл стилей xl/styles.xml.ЧтениеXML и ЗаписьXML (или ПостроительDOM) вносим изменения: удаляем у ячеек с датами атрибут t="s" (который указывает, что значение текстовое) и заменяем строковые значения на числовые идентификаторы дат.ЗаписьZipФайла.Этот метод полностью исключает влияние платформенных ограничений 1С на итоговый файл и дает на выходе «чистокровный» Excel-документ с аппаратной поддержкой типов данных.
Если формирование отчета происходит на стороне клиента под управлением ОС Windows с установленным пакетом Microsoft Office, мы можем прибегнуть к автоматизации через COM-объект. Это решает задачу буквально в несколько строк кода за счет вызова внутренних макросов Excel.
Посмотрим на пример реализации кода:
ПутьКФайлу = "C:\Reports\Report.xlsx";
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
Попытка
ExcelПриложение = Новый COMОбъект("Excel.Application");
ExcelПриложение.Visible = Ложь;
ExcelПриложение.DisplayAlerts = Ложь;
Книга = ExcelПриложение.Workbooks.Open(ПутьКФайлу);
Лист = Книга.Sheets(1);
// Самый быстрый способ заставить Excel перечитать текстовые даты как системные:
// Производим фиктивную замену точек в датах на точки. Excel принудительно выполнит парсинг.
Лист.Cells.Replace(".", ".", 2); // 2 соответствует xlPart
Книга.Save();
Книга.Close();
ExcelПриложение.Quit();
Исключение
Сообщить("Не удалось запустить Excel для постобработки данных: " + ОписаниеОшибки());
Если ЗначениеЗаполнено(ExcelПриложение) Тогда
ExcelПриложение.Quit();
КонецЕсли;
КонецПопытки;
Использование метода Replace(".", ".") заставляет движок Excel пересчитать текстовое содержимое ячеек. Если строка соответствует формату даты, принятому в региональных настройках операционной системы, Excel моментально конвертирует ее в полноценный тип даты.
Если файл уже передан пользователю, и ему нужно оперативно исправить текстовый формат дат на стандартный системный, поделитесь с ним простыми инструкциями:
Вариант А. Инструмент «Текст по столбцам»
Вариант Б. Специальная вставка (сложение с нулем)
0 и скопируйте эту ячейку (Ctrl+C).Мы подробно разобрали все технические нюансы выгрузки дат из 1С в Excel. Выбирайте наиболее подходящий метод исходя из архитектуры вашего решения (наличие Windows/Linux на сервере, требования к производительности и необходимость автономной работы системы) и делайте ваши отчеты максимально удобными для конечных пользователей!