При интеграции 1С с внешними базами данных, такими как Firebird, или при загрузке данных из Microsoft Excel, разработчики часто сталкиваются с тем, что дата хранится не в привычном строковом или типизированном виде, а как вещественное или целое число. Это число представляет собой количество дней, прошедших с определенной «нулевой точки» (эпохи). Однако при попытке простого прибавления этого числа к базовой дате в 1С результат часто оказывается смещенным на 1 или 2 дня. Разберем подробно, почему это происходит и как написать универсальный алгоритм преобразования.
Проанализируем ситуацию, описанную в теме. Автор столкнулся с тем, что число 43815 в Excel соответствует 16.12.2019, а в 1С при расчете от 01.01.1900 получается 18.12.2019. Куда делись 2 дня? Разберем по шагам природу этого явления.
Причина №1: Ошибка високосного года в Excel
Исторически сложилось так, что разработчики Microsoft Excel для обеспечения совместимости с тогдашним лидером рынка — табличным редактором Lotus 1-2-3 — намеренно сохранили ошибку в расчете високосных лет. В системе Lotus 1-2-3 год 1900-й считался високосным. Согласно же григорианскому календарю, год, делящийся на 100, является високосным только в том случае, если он также делится на 400. Следовательно, 1900 год не был високосным, и даты 29 февраля 1900 года в реальности не существовало. Однако Excel считает этот день. Из-за этого все даты, начиная с 1 марта 1900 года, в Excel сдвинуты на один день вперед относительно строгого астрономического календаря, который использует 1С.
Причина №2: Разные точки отсчета (Базовая дата)
Разные системы используют разные даты в качестве «нулевого» дня:
TDateTime также отсчитывает дни от 30 декабря 1899 года.Таким образом, если мы берем 01.01.1900 вместо 30.12.1899, мы уже получаем разницу в 2 дня (31 декабря и 1 января).
Рассмотрим, как правильно реализовать преобразование числа в дату. Нам необходимо учитывать, что в 1С арифметические операции с датами производятся в секундах. Поэтому количество дней, полученное из внешней системы, необходимо умножать на 86400 (количество секунд в сутках).
Посмотрим на пример кода, который решает проблему сдвига путем использования правильной базовой даты:
// Входящий параметр ЧислоДнейИзВне - это наше число (например, 43815)
БазоваяДата = Дата("18991230"); // Используем 30 декабря 1899 года
Результат = БазоваяДата + (ЧислоДнейИзВне * 86400);
// Для числа 43815 результатом будет 16.12.2019
Почему мы выбрали именно 30.12.1899? Проанализируем ситуацию: если мы прибавим 1 день к этой дате, мы получим 31.12.1899. Если прибавим 2 дня — 01.01.1900. Это позволяет нивелировать разницу в «точках старта» между 1С и COM-моделью Windows.
Некоторые разработчики предпочитают использовать в качестве точки отсчета 31 декабря. В этом случае формула будет выглядеть так, как предложил автор во втором сообщении темы:
Функция ПреобразоватьЧислоВДату(Знач ДатаВЧисле) Экспорт
БазоваяДата = Дата("18991231");
// Если мы используем 31.12.1899, то нужно быть внимательным с тем,
// является ли 1-й день в источнике началом отсчета или уже прошедшим днем.
Возврат БазоваяДата + (ДатаВЧисле * 86400);
КонецФункции
Однако стоит помнить, что самый надежный способ — это эмпирическая проверка. Если после сложения вы все еще видите разницу в 1 день, значит, источник данных (например, специфическая настройка Firebird или драйвера ODBC) учитывает или не учитывает тот самый несуществующий день 29.02.1900.
Проанализируем более сложную ситуацию, когда нам нужно гарантировать точность для дат до 1900 года и после. На практике в бизнес-задачах мы редко сталкиваемся с датами ранее 1900 года, поэтому наиболее стабильным решением для 1С будет следующая логика:
01.01.1900 и вычитаем эти 2 дня, либо сразу используем 30.12.1899.Пример функции, учитывающей особенности хранения в Firebird (где дата часто передается как количество дней от 01.01.1900):
Функция ПолучитьДатуИзЧислаFirebird(ЧислоДней) Экспорт
// Firebird хранит дни, Excel также считает их от начала века.
// Вычитаем 2 дня для компенсации разницы эпох и ошибки високосного года.
КоличествоСекунд = (ЧислоДней - 2) * 86400;
Возврат Дата("19000101") + КоличествоСекунд;
КонецФункции
Разберем еще один важный момент, упомянутый в обсуждении. В Firebird дата может храниться по-разному в зависимости от версии диалекта (Dialect). В современном Dialect 3 тип DATE — это действительно 32-битное целое число. Однако при получении данных через провайдеры (например, ADODB), система может попытаться самостоятельно преобразовать это число в тип Date языка запросов. Если вы получаете уже преобразованный объект, но он отображается некорректно, проверьте настройки региональных стандартов вашей операционной системы.
Если же вы работаете с сырыми данными, помните:
Дата() в 1С очень строгая. Нельзя передать в нее Дата("19000100") — это вызовет ошибку, так как нулевого дня месяца не существует.2019-2024 годов это не является проблемой.Подводя итог, можно сказать: путаница с датами возникает не из-за ошибок в платформе 1С, а из-за необходимости поддерживать совместимость со старыми стандартами вычислений в продуктах Microsoft и Lotus. Используя базовую дату 30.12.1899, вы сможете корректно импортировать данные из большинства внешних источников.