Работа с временными интервалами является одной из фундаментальных задач при разработке конфигураций на платформе 1С:Предприятие. Разработчикам постоянно приходится рассчитывать сроки действия договоров, сроки годности товаров или периоды отчетности. Операция вычитания времени из конкретной даты кажется тривиальной, но на практике она содержит множество подводных камней, особенно при переходе через границы месяцев и високосные годы.

В данной статье мы детально разберем синтаксис языка запросов и встроенного языка 1С, позволяющий выполнять арифметические операции над датами. Мы рассмотрим разницу между простым вычитанием дней и сложной логикой вычитания месяцев, а также проанализируем, как система обрабатывает несуществующие даты, возникающие в результате таких операций.

Понимание этих механизмов критически важно для написания корректного кода. Ошибки в расчете дат могут привести к неверному начислению зарплаты, неправильному формированию налоговых регистров или сбоям в работе регламентных заданий. Поэтому важно не просто знать синтаксис, но и понимать внутреннюю логику работы движка 1С с типом данных Дата.

Арифметика дат и базовый синтаксис

В языке запросов 1С тип данных Дата поддерживает прямые арифметические операции. Самый простой способ уменьшить дату — это вычесть из неё числовое значение, которое интерпретируется как количество дней. Этот метод работает предсказуемо и линейно, сдвигая дату назад на указанное количество суток.

Например, если вам нужно найти дату, которая была ровно 10 дней назад от текущей даты получения документа, вы можете использовать следующую конструкцию в тексте запроса. Здесь переменная ДатаДокумента уменьшается на константу или поле таблицы.

ВЫБРАТЬ

Документ.Ссылка,

ДОБАВИТЬКДАТЕ(Документ.Дата, -10, ДЕНЬ) КАК ДатаМинус10Дней

ИЗ

Документ.РеализацияТоваровУслуг КАК Документ

Однако, использование функции ДОБАВИТЬКДАТЕ с отрицательным значением является более предпочтительным и читаемым способом, чем прямое вычитание чисел в сложных выражениях. Функция явно указывает на намерение разработчика изменить временную метку на определенный интервал.

При работе во встроенном языке (код в модулях) синтаксис остается схожим, но требует явного указания типа интервала. Вы можете вычитать дни, минуты или секунды, просто используя оператор минус, если работаете с днями, но для других интервалов лучше использовать встроенные функции.

  • 📅 Вычитание дней: НоваяДата = Дата - 5 сдвинет дату на 5 суток назад.
  • ⏱️ Вычитание времени: для работы с часами и минутами лучше преобразовывать дату в число или использовать специальные функции.
  • 🔄 Обратимость: операция вычитания полностью обратима добавлением того же интервала.
  • 🧮 Тип данных: результат операции всегда остается типом Дата, даже если время обнуляется.
💡

При вычитании дней из даты учитывается високосный год автоматически. Если вы вычитаете 366 дней из даты 1 марта 2026 года, вы получите 1 марта 2023 года, а не 29 февраля.

Использование функции ДОБАВИТЬКДАТЕ для вычитания

Функция ДОБАВИТЬКДАТЕ (или DateAdd в английской версии) является универсальным инструментом для манипуляций со временем. Несмотря на название "Добавить", она идеально подходит для вычитания, если передать отрицательное значение в параметр количества интервалов.

Синтаксис функции требует указания трех параметров: исходной даты, типа интервала и количества этих интервалов. Тип интервала задается предопределенными значениями, такими как ГОД, МЕСЯЦ, ДЕНЬ, ЧАС, МИНУТА, СЕКУНДА. Это позволяет гибко управлять granularity вычислений.

Рассмотрим пример вычитания одного месяца. Это операция сложнее, чем вычитание дней, так как количество дней в месяцах различается. Если вы вычитаете месяц из даты 31 марта, результатом будет 28 или 29 февраля, в зависимости от года. Система автоматически корректирует день месяца, если полученная дата не существует.

ИсходнаяДата = '20230331';

// Вычитаем один месяц

НоваяДата = ДОБАВИТЬКДАТЕ(ИсходнаяДата, -1, МЕСЯЦ);

// Результат: 20230228 (или 20230229 в високосный год)

⚠️ Внимание: Поведение функции при переходе через конец месяца может отличаться в старых версиях платформы. Всегда тестируйте логику на пограничных датах (30-е и 31-е числа), если ваша конфигурация должна работать на релизах ниже 8.3.10.

Функция изменяет только ту часть даты, которая указана в типе интервала, и корректирует младшие разряды только в случае переполнения (например, 32-го дня не существует).

  • 📉 Отрицательное число: ключевой параметр для операции вычитания в функции.
  • 🗓️ Тип интервала: определяет, какая часть даты будет изменена (год, месяц или день).
  • 🛡️ Безопасность: функция гарантирует возврат корректной даты, даже если день выходит за рамки месяца.
  • ⏳ Сохранение времени: часы и минуты остаются теми же, если не вычитаются явно.
📊 Какой метод вы используете чаще всего?
Прямое вычитание чисел
Функция ДОБАВИТЬКДАТЕ
Конструктор запросов
Встроенные функции периода

Особенности вычитания месяцев и лет

Вычитание крупных интервалов, таких как месяцы и годы, требует особого внимания разработчика. В отличие от дней, которые имеют фиксированную длительность (24 часа), месяцы имеют разную длину. Это создает ситуацию неоднозначности, которую платформа 1С разрешает по определенным правилам.

Когда вы вычитаете месяц из даты, попадающей на конец длинного месяца (30 или 31 число), система пытается подставить то же число в предыдущий месяц. Если такого числа нет (например, 31 февраля), дата автоматически сдвигается на последний доступный день этого месяца.

Эта логика распространяется и на вычитание лет. При переходе через 29 февраля високосного года назад на обычный год, дата 29 февраля превратится в 28 февраля. Это поведение зашито в ядро платформы и не требует дополнительной проверки кода.

Исходная дата Операция Результат (не високосный год) Результат (високосный год)
31 Марта - 1 Месяц 28 Февраля 29 Февраля
31 Января - 1 Месяц 31 Декабря 31 Декабря
29 Февраля - 1 Год 28 Февраля 29 Февраля
30 Августа - 1 Месяц 30 Июля 30 Июля

Разработчикам следует учитывать этот механизм при расчете сроков действия лицензий или договоров, заключенных в конце месяца. Если договор заключен 31 числа и действует 1 месяц, его истечение может наступить 30-го или 28-го числа следующего месяца в зависимости от календаря.

Почему 31 марта минус месяц это 28 февраля?

Платформа 1С следует логике "последнего дня месяца". Поскольку 31 февраля не существует, система выбирает максимально возможную дату в целевом месяце, чтобы минимизировать потерю времени.

Работа с началом и концом периодов

Часто при вычитании дат необходимо получить не просто сдвинутую дату, а границу периода. Для этого в 1С существуют специальные функции НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА. Они позволяют нормализовать дату после вычитания интервала.

Например, если вам нужно получить дату начала месяца, который был два месяца назад, недостаточно просто вычесть два месяца. Нужно еще обрезать день, час и минуту до нуля. Комбинация функций вычитания и нормализации периода решает эту задачу эффективно.

ТекущаяДата = ТЕКУЩАЯДАТА();

// Вычитаем 2 месяца и получаем начало того месяца

ДатаНачала = НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(ТекущаяДата, -2, МЕСЯЦ), МЕСЯЦ);

Использование начала периода критически важно при формировании выборок из регистров накопления. Остатки на регистрах обычно фиксируются на конкретный момент времени, и "плавающая" дата с минутами и секундами может привести к тому, что нужный срез не попадет в выборку.

Функция КОНЕЦПЕРИОДА полезна, когда нужно вычесть период и получить дату, включающую весь последний день. Например, для расчета срока годности товара, который истекает в конце месяца через год.

  • 🚦 Нормализация: сброс времени в 00:00:00 для корректного сравнения.
  • 📏 Границы: получение точных дат начала и конца для отчетов.
  • 🧩 Комбинирование: вложение функций позволяет создавать сложные временные конструкции.
  • 📑 Срезы регистров: обязательное использование для работы с виртуальными таблицами.

⚠️ Внимание: Функции начала и конца периода работают только с целыми значениями интервалов (день, месяц, год, квартал). Нельзя получить "начало 15 минут", но можно получить начало часа.

Обработка ошибок и несуществующих дат

Хотя платформа 1С автоматически корректирует большинство ситуаций с несуществующими датами, существуют сценарии, требующие ручной проверки. В частности, это касается пользовательского ввода или загрузки данных из внешних источников, где дата может быть сформирована некорректно до попадания в систему.

При вычитании интервалов в цикле или в сложных алгоритмах накопления ошибок не происходит, так как тип Дата в 1С имеет широкий диапазон значений. Однако логические ошибки возможны, если разработчик ожидает сохранения номера дня месяца при вычитании месяцев.

Для валидации дат можно использовать функцию ЭТОДАТА или попытку присвоения значения. Если дата выходит за допустимые пределы платформы (что маловероятно при обычном вычитании), система выдаст исключение.

☑️ Проверка корректности вычитания

Выполнено: 0 / 4

Особое внимание стоит уделить "нулевой" дате. В 1С это дата 0001.01.01. Вычитание интервалов из неё может привести к ошибкам или неопределенному поведению в зависимости от контекста выполнения кода. Всегда проверяйте дату на пустоту перед выполнением арифметических операций.

Производительность операций с датами в запросах

Выполнение функций работы с датами непосредственно в тексте запроса может влиять на производительность, особенно при обработке больших объемов данных. Использование функций в условиях соединения (JOIN) или в полях выборки требует вычислительных ресурсов сервера.

Наиболее оптимальным подходом является фильтрация по датам с использованием готовых границ, рассчитанных в коде перед выполнением запроса. Это позволяет использовать индексы по полям даты и избегать.full scan таблиц.

Если же вычисление даты необходимо в самом запросе (например, для группировки по периодам), убедитесь, что функция ДОБАВИТЬКДАТЕ применяется к полям, а не к константам, где это возможно, или используйте временные таблицы для промежуточных расчетов.

💡

Вынесение расчетов дат из тела запроса в код программы часто ускоряет выполнение запроса в разы за счет использования индексов базы данных.

В высоконагруженных системах стоит избегать вложенных вызовов функций даты внутри условий ГДЕ. Лучше рассчитать целевую дату в переменную и передать её в запрос как параметр. Это упрощает план выполнения запроса оптимизатором СУБД.

FAQ: Часто задаваемые вопросы

Как вычесть из даты ровно 30 дней, игнорируя календарь?

Для этого используйте простое арифметическое вычитание числа из даты: НоваяДата = Дата - 30. В этом случае 1С просто отнимет 30 * 24 часа от временной метки, не учитывая переходы между месяцами разной длины.

Что вернет функция, если вычесть месяц из 31 января?

Функция ДОБАВИТЬКДАТЕ(Дата, -1, МЕСЯЦ) вернет 31 декабря предыдущего года. Поскольку в декабре 31 день существует, коррекция не требуется. Проблема возникает только когда целевой месяц короче исходного дня.

Можно ли вычитать секунды в языке запросов?

Да, в качестве третьего параметра функции ДОБАВИТЬКДАТЕ можно указать предопределенное значение СЕКУНДА. Это полезно для точных расчетов времени в системах учета рабочего времени или логирования событий.

Как получить дату вчерашнего дня без времени?

Используйте комбинацию функций: НАЧАЛОДНЯ(ТЕКУЩАЯДАТА() - 1). Это вычтет один день и обрежет время до начала суток, гарантируя получение вчерашней даты с временем 00:00:00.

Влияет ли часовой пояс на вычитание дат?

В пределах одного сервера 1С время хранится в UTC, но при отображении и вводе конвертируется в локальное. Арифметические операции выполняются над внутренним представлением. При вычитании дней смена часовых поясов (летнее/зимнее время) не влияет на результат в сутках, но может сдвинуть время на час при работе с часами.