При разработке конфигураций в платформе 1С:Предприятие 8 программисты часто сталкиваются с необходимостью работы с типом Дата. Этот тип данных по своей природе всегда содержит и календарную дату, и точное время с точностью до секунды. Однако в бизнес-задачах часто требуется сравнить только даты, игнорируя часовые и минутные показатели, например, для формирования отчетов по периодам или фильтрации документов.
Если просто присвоить одну переменную другой, временная составляющая сохранится, что может привести к логическим ошибкам при сравнении или группировке данных. Существует несколько надежных способов «обрезать» время, оставив только дату. Выбор конкретного метода зависит от версии платформы, контекста задачи и требований к производительности кода.
В этой статье мы детально разберем встроенные функции платформы, математические операции и особенности работы с типами данных. Вы узнаете, как корректно обрабатывать временные метки, чтобы ваши выборки и расчеты всегда были точными и предсказуемыми.
Метод НачалоДня: стандартный подход платформы
Самым надежным и рекомендуемым разработчиками 1С способом получения даты без времени является использование встроенной функции НачалоДня(). Эта функция принимает значение типа Дата и возвращает новую дату, у которой время установлено в 00:00:00. Она работает предсказуемо на любых версиях платформы и не требует дополнительных вычислений.
Использование этой функции делает код читаемым и понятным для других разработчиков. Когда вы видите НачалоДня(ДатаДокумента), сразу становится ясно, что программист намеренно отбрасывает время. Это соответствует стандартам разработки и упрощает поддержку конфигурации в будущем.
Функция корректно обрабатывает переходы через полночь и не зависит от системных настроек часового пояса в том виде, в котором это делает работа с таймерами. Результат всегда будет строго началом суток указанной даты.
⚠️ Внимание: ФункцияНачалоДнявозвращает значение типа Дата, а не Строка. Если вам нужно отобразить дату в отчете без времени, форматирование следует выполнять отдельно через функциюФормат().
Всегда используйте НачалоДня() при формировании периодов для отчетов, чтобы избежать дублирования записей из-за разного времени создания документов.
Пример использования функции в коде выглядит следующим образом:
ДатаСВременем = ТекущаяДата();
ДатаБезВремени = НачалоДня(ДатаСВременем);
Сообщить(ДатаБезВремени); // Выведет дату вида 25.10.2023 0:00:00
Такой подход гарантирует, что даже если исходная дата имела время 23:59:59, результат будет сброшен к началу суток. Это критически важно при сравнении дат в условиях запросов или операторов Если.
Использование функции Час для обнуления времени
Альтернативным, хотя и менее удобным методом, является использование конструктора даты вместе с функцией извлечения компонентов. Вы можете явно указать, что часы, минуты и секунды должны быть равны нулю. Для этого сначала нужно получить год, месяц и день из исходной даты.
Этот метод требует больше строк кода и выглядит громоздко по сравнению с НачалоДня(). Однако он может быть полезен в ситуациях, когда нужно не просто обнулить время, а изменить его на конкретное значение, например, установить 12:00:00 для усреднения.
Для извлечения компонентов используются функции Год(), Месяц() и День(). Затем эти значения передаются в конструктор Дата(), где последние три аргумента (часы, минуты, секунды) задаются явно как ноль.
- 📅 Функция
Год(Дата)возвращает числовое значение года. - 📅 Функция
Месяц(Дата)возвращает номер месяца от 1 до 12. - 📅 Функция
День(Дата)возвращает номер дня в месяце.
Код реализации этого подхода будет выглядеть так:
ИсходнаяДата = ТекущаяДата();
НоваяДата = Дата(Год(ИсходнаяДата), Месяц(ИсходнаяДата), День(ИсходнаяДата), 0, 0, 0);
Хотя результат будет идентичен использованию НачалоДня(), читаемость такого кода ниже. Разработчику приходится анализировать каждый аргумент конструктора, чтобы понять суть операции.
Математические операции с типом Дата
В языке 1С тип Дата поддерживает арифметические операции. Дату можно представлять как количество секунд, прошедших с некоторой эпохи. Зная это, можно попытаться обнулить время путем вычисления остатка от деления или вычитания прошедших секунд.
В сутках содержится 86400 секунд (24 часа × 60 минут × 60 секунд). Если мы возьмем полное количество секунд в дате и найдем остаток от деления на 86400, мы получим количество секунд, прошедших с начала текущих суток. Вычтя это значение из исходной даты, мы получим начало дня.
Однако этот метод считается «плохим тоном» в современной разработке 1С. Он менее производителен, так как требует дополнительных вычислений, и менее понятен при чтении кода. Кроме того, он может вести себя некорректно при переходе через високосные секунды или в специфических часовых поясах.
⚠️ Внимание: Избегайте математических трюков с датами в высоконагруженных системах. Лишние вычисления секунд могут замедлить обработку больших массивов данных в циклах.
Тем не менее, понимание этого механизма полезно для отладки. Пример реализации через вычисление секунд:
СекундВДне = 86400;
ВремяВСекундах = Время(Час(Дата), Минута(Дата), Секунда(Дата));
// Вычитаем время, приведенное к секундам, из даты
ДатаБезВремени = Дата - ВремяВСекундах;
Как видно, код становится сложнее и требует понимания внутренней структуры хранения дат. Использование встроенных функций платформы всегда предпочтительнее.
Сравнение дат и проблемы точности
Одной из самых частых ошибок при работе с датами является попытка сравнить две даты, у одной из которых время обнулено, а у другой — нет. Если вы используете операторы сравнения =, < или >, платформа будет учитывать каждую секунду.
Например, дата 25.10.2023 00:00:00 не равна дате 25.10.2023 10:30:00, хотя визуально для пользователя это один и тот же день. В отчетах это может привести к тому, что документы, созданные в течение дня, не попадут в выборку, если условие написано неверно.
Для корректного сравнения необходимо приводить обе сравниваемые величины к единому формату. Лучше всего использовать НачалоДня() для обеих переменных перед сравнением. Это гарантирует, что сравнение будет происходить строго по календарным суткам.
| Ситуация | Дата 1 | Дата 2 | Результат сравнения (=) |
|---|---|---|---|
| Без обработки | 25.10.2023 00:00:00 | 25.10.2023 15:00:00 | Ложь |
| С НачалоДня | НачалоДня(25.10...) | НачалоДня(25.10...) | Истина |
| Разные дни | 25.10.2023 23:59:59 | 26.10.2023 00:00:01 | Ложь |
| Граница суток | НачалоДня(25.10...) | НачалоДня(26.10...) | Ложь |
Особое внимание стоит уделить диапазонам дат. Если вы формируете период «с» и «по», для даты «по» часто используют функцию КонецДня() или добавляют один день к началу следующего периода и используют знак «меньше».
При сравнении дат всегда приводите их к началу дня с помощью НачалоДня(), иначе сравнение будет учитывать время и даст неверный результат.
Работа с датами в запросах 1С
В языке запросов 1С также существует возможность работы с датами без времени. Однако синтаксис отличается от встроенного языка. В запросах часто используется приведение типов или специальные функции, доступные в конкретной версии платформы.
Если вы передаете параметр в запрос, лучше всего подготовить его на стороне встроенного языка перед выполнением запроса. Это снижает нагрузку на сервер баз данных и упрощает логику выборки. Передавайте в запрос уже «очищенную» дату.
В тексте запроса можно использовать конструкцию НАЧАЛОДНЯ(), если СУБД и версия платформы это поддерживают напрямую в выражениях. Но наиболее универсальный способ — использование параметров.
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Документ.Ссылка
| ИЗ Документ.РеализацияТоваровУслуг КАК Документ
| ГДЕ НачалоДня(Документ.Дата) = &Период";
Запрос.УстановитьПараметр("Период", НачалоДня(ВыбраннаяДата));
Результат = Запрос.Выполнить();
Такой подход обеспечивает максимальную совместимость и производительность. Сервер 1С сам оптимизирует выполнение запроса, используя индексы по полю Дата, если они построены корректно.
⚠️ Внимание: Функции в условииГДЕзапроса (например,НАЧАЛОДНЯ(Документ.Дата)) могут препятствовать использованию индексов, что замедлит работу на больших объемах данных. Лучше сравнивать диапазоном:Документ.Дата >= &Начало И Документ.Дата < &Конец.
Почему индексы не работают с функциями?
Когда вы применяете функцию к полю в условии WHERE, базе данных приходится вычислять эту функцию для каждой строки таблицы, прежде чем сравнить результат. Это называется сканированием таблицы, и оно очень медленное. Индексы работают только при прямом сравнении значения поля с константой или параметром.
Форматирование даты для вывода пользователю
Часто задача «получить дату без времени» возникает не для вычислений, а для отображения в печатных формах или интерфейсе. В этом случае не обязательно менять само значение переменной. Достаточно изменить формат строки представления.
Для этого используется функция Формат(). Она позволяет задать шаблон вывода, в котором можно указать, что нужно показывать только день, месяц и год. При этом внутреннее значение переменной останется типом Дата со временем, что удобно для дальнейших расчетов.
Строка формата выглядит как "ДФ='dd.MM.yyyy'". Буквы ДФ означают «Дата Формат», а последующие символы задают порядок вывода компонентов. Это стандартный подход для отчетов и печатных форм.
- 🖨
dd— день месяца с ведущим нулем (01, 02...). - 🖨
MM— номер месяца с ведущим нулем (01, 12...). - 🖨
yyyy— год четырехзначным числом.
Пример кода для подготовки строки к выводу:
ТекущаяДата = ТекущаяДата();
СтрокаДаты = Формат(ТекущаяДата, "ДФ='dd.MM.yyyy'");
Сообщить(СтрокаДаты); // Выведет "25.10.2023" как строку
Помните, что результат функции Формат — это тип Строка. С ней нельзя выполнять арифметические операции или сравнивать её с датами как с датами. Используйте этот метод только для финального отображения.
☑️ Проверка перед внедрением
Частые ошибки и рекомендации
Новички часто пытаются преобразовать дату в строку, обрезать лишние символы и снова преобразовать в дату. Это крайне неэффективный путь, который приводит к ошибкам парсинга и зависимостям от региональных настроек пользователя.
Еще одна ошибка — игнорирование часовых поясов при работе с веб-сервисами или файловыми версиями. Время может сдвигаться при сохранении и загрузке данных. Всегда проверяйте, в каком часовом поясе хранится дата в базе.
Используйте типизированные переменные. Не храните даты в строковых полях базы данных, если планируете по ним делать отборы. Это лишает вас преимуществ индексации и скорости работы СУБД.
В чем разница между НачалоДня и КонецДня?
Функция НачалоДня() устанавливает время в 00:00:00 выбранной даты. Функция КонецДня() устанавливает время в 23:59:59 той же даты. Они часто используются в паре для формирования диапазона периода: от начала одного дня до конца другого.
Что вернет функция, если передать ей строку?
Функции работы с датой (НачалоДня, Год и др.) ожидают на входе тип Дата. Если передать строку, возникнет ошибка выполнения. Сначала необходимо преобразовать строку в дату функцией Дата() или Попытка...Исключение.
Как получить вчерашнюю дату без времени?
Комбинируйте вычитание дней и функцию начала суток: НачалоДня(ТекущаяДата() - 1). Сначала отнимается один день (вместе со временем), затем результат округляется до начала полученных суток.
Можно ли хранить дату без времени в базе данных?
В таблицах базы данных 1С тип поля всегда Дата, который физически хранит и время. Невозможно создать поле, которое хранит только дату. Обнуление времени происходит программно при чтении или записи данных.
Влияет ли летнее время на работу функций даты?
В современных версиях платформы 1С работа с датами учитывает переходы на летнее/зимнее время автоматически, если на сервере настроены корректные часовые пояса. Функция НачалоДня всегда вернет локальное начало суток для указанного момента.