Как правильно перевести числовое представление даты из Firebird или Excel в формат 1С и почему возникает разница в 2 дня?

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

При интеграции 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: Разные точки отсчета (Базовая дата)
Разные системы используют разные даты в качестве «нулевого» дня:

Таким образом, если мы берем 01.01.1900 вместо 30.12.1899, мы уже получаем разницу в 2 дня (31 декабря и 1 января).

Решение проблемы в 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.12.1899

Некоторые разработчики предпочитают использовать в качестве точки отсчета 31 декабря. В этом случае формула будет выглядеть так, как предложил автор во втором сообщении темы:


Функция ПреобразоватьЧислоВДату(Знач ДатаВЧисле) Экспорт
    БазоваяДата = Дата("18991231");
    // Если мы используем 31.12.1899, то нужно быть внимательным с тем, 
    // является ли 1-й день в источнике началом отсчета или уже прошедшим днем.
    Возврат БазоваяДата + (ДатаВЧисле * 86400);
КонецФункции

Однако стоит помнить, что самый надежный способ — это эмпирическая проверка. Если после сложения вы все еще видите разницу в 1 день, значит, источник данных (например, специфическая настройка Firebird или драйвера ODBC) учитывает или не учитывает тот самый несуществующий день 29.02.1900.

Универсальный алгоритм для импорта данных

Проанализируем более сложную ситуацию, когда нам нужно гарантировать точность для дат до 1900 года и после. На практике в бизнес-задачах мы редко сталкиваемся с датами ранее 1900 года, поэтому наиболее стабильным решением для 1С будет следующая логика:

  1. Определяем входящее число дней.
  2. Если используется формат Excel/Delphi, принимаем за константу разницу в 2 дня относительно «чистого» календаря 1С.
  3. Выполняем расчет от даты 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С, а из-за необходимости поддерживать совместимость со старыми стандартами вычислений в продуктах Microsoft и Lotus. Используя базовую дату 30.12.1899, вы сможете корректно импортировать данные из большинства внешних источников.

← На главную