Дата и время в SQL Server — группа типов данных для хранения момента времени: календарной даты, времени суток или того и другого вместе, с точностью от суток (для типа с точностью до дня) до 100 наносекунд (для типов с максимальной точностью). У каждого типа свой диапазон и размер в байтах, а у T-SQL есть отдельные функции для текущего момента, разбора на части, арифметики и перевода между часовыми поясами. Разберем все это по порядку, с проверкой в SQL Server 2016 и новее (актуально и для Azure SQL Database).
Содержание
- Типы данных для даты и времени
- Текущая дата и время: GETDATE, SYSDATETIME и UTC
- Извлечение частей даты: DATEPART, DATENAME, YEAR/MONTH/DAY
- Арифметика дат: DATEADD и DATEDIFF
- Преобразование и форматирование: CONVERT и FORMAT
- DATETIME2 вместо DATETIME: что выбрать
- Часовые пояса: DATETIMEOFFSET и AT TIME ZONE
- Выводы
- Где применяется / связь с практикой
- FAQ
Типы данных для даты и времени
В SQL Server шесть типов, специально предназначенных для даты и времени. Диапазон и размер напрямую влияют на то, сколько места займет таблица и какие значения в нее вообще получится записать.
| Тип | Диапазон | Точность | Размер |
|---|---|---|---|
| DATE | 0001-01-01 — 9999-12-31 | 1 день | 3 байта |
| TIME(n) | 00:00:00.0000000 — 23:59:59.9999999 | до 100 нс, n = 0-7 | 3-5 байт (зависит от n) |
| SMALLDATETIME | 1900-01-01 — 2079-06-06 | 1 минута | 4 байта |
| DATETIME | 1753-01-01 — 9999-12-31 | округление до .000/.003/.007 сек | 8 байт |
| DATETIME2(n) | 0001-01-01 — 9999-12-31 | до 100 нс, n = 0-7 | 6-8 байт (зависит от n) |
| DATETIMEOFFSET(n) | 0001-01-01 — 9999-12-31, плюс смещение от UTC -14:00…+14:00 | до 100 нс, n = 0-7 | 8-10 байт (зависит от n) |
Для TIME, DATETIME2 и DATETIMEOFFSET размер зависит от заявленной точности (параметр n — число знаков после запятой у секунд): при n от 0 до 2 TIME занимает 3 байта, DATETIME2 — 6, DATETIMEOFFSET — 8; при n от 3 до 4 — соответственно 4, 7 и 9 байт; при n от 5 до 7 (максимум) — 5, 8 и 10 байт. Без явного указания n по умолчанию берется 7.
Если нужна только календарная дата (день рождения, дата документа) — берите DATE, а не DATETIME с обнуленным временем: это и компактнее, и честнее по смыслу поля.
Текущая дата и время: GETDATE, SYSDATETIME и UTC
В T-SQL нет функций NOW() или CURDATE() — это синтаксис MySQL, в SQL Server он не работает. Текущий момент получают так:
SELECT
GETDATE() AS local_datetime, -- тип datetime, округление до .000/.003/.007 сек
SYSDATETIME() AS local_datetime2, -- тип datetime2(7), точность до 100 нс
GETUTCDATE() AS utc_datetime, -- то же самое, но в UTC
SYSUTCDATETIME() AS utc_datetime2, -- UTC с точностью datetime2
SYSDATETIMEOFFSET() AS local_with_offset; -- datetime2 + смещение часового пояса сервера
Все пять функций читают системные часы сервера (или контейнера/ВМ, где он запущен), а не клиента. Если приложение и СУБД стоят в разных часовых поясах, разница будет только в SYSDATETIMEOFFSET — остальные вернут «голое» время без пометки, для какого пояса оно посчитано.
Извлечение частей даты: DATEPART, DATENAME, YEAR/MONTH/DAY
Чтобы достать из даты отдельную часть, в T-SQL есть DATEPART (число) и DATENAME (текст), а для года/месяца/дня — короткие функции-обертки:
DECLARE @d datetime2 = '2026-09-19 14:30:00';
SELECT
YEAR(@d) AS y, -- 2026
MONTH(@d) AS m, -- 9
DAY(@d) AS d, -- 19
DATEPART(weekday, @d) AS wd, -- зависит от DATEFIRST сессии
DATENAME(month, @d) AS mname; -- "September" или "Сентябрь" - зависит от языка сессии
DATENAME и текстовое представление DATEPART(weekday, …) зависят от параметров SET LANGUAGE и SET DATEFIRST текущей сессии — один и тот же запрос на разных подключениях может вернуть разный день недели по номеру и разное название месяца.
Арифметика дат: DATEADD и DATEDIFF
DATEADD прибавляет (или вычитает, если число отрицательное) целое число единиц к дате:
SELECT DATEADD(day, 30, '2026-09-19'); -- 2026-10-19
SELECT DATEADD(month, -1, '2026-09-19'); -- 2026-08-19
DATEDIFF считает не «сколько ровно времени прошло», а сколько раз была пересечена граница указанной единицы измерения. Это регулярно дает контринтуитивный результат:
DECLARE @a datetime2 = '2026-09-19 23:59:59';
DECLARE @b datetime2 = '2026-09-20 00:00:01';
SELECT DATEDIFF(minute, @a, @b) AS minutes_wrong; -- вернет 1, хотя прошло всего 2 секунды
Разница всего в 2 секунды, но между значениями лежит граница минуты (и суток), поэтому DATEDIFF(minute, …) засчитывает целую минуту. Чтобы получить точный интервал, нужно спуститься на уровень секунд (или меньше) и уже потом пересчитать:
SELECT DATEDIFF(second, @a, @b) / 60.0 AS minutes_precise; -- 0.033333 - именно 2 секунды в минутах
Для очень больших интервалов (миллисекунды/микросекунды/наносекунды между датами, разнесенными на годы) обычный DATEDIFF может переполнить INT. Начиная с SQL Server 2016 для этого есть DATEDIFF_BIG — та же логика, но результат BIGINT.
Преобразование и форматирование: CONVERT и FORMAT
CONVERT переводит дату в строку по числовому коду стиля — работает быстро и годится для условий WHERE:
SELECT
CONVERT(varchar(10), GETDATE(), 23) AS iso_date, -- 2026-09-19
CONVERT(varchar(19), GETDATE(), 120) AS odbc_canonical; -- 2026-09-19 14:30:00
FORMAT() (с SQL Server 2012) гибче — принимает .NET-формат и учитывает культуру (FORMAT(GETDATE(), 'dd MMMM yyyy', 'ru-RU')), но заметно медленнее CONVERT и не sargable: индекс по дате при фильтрации FORMAT в WHERE не используется. Для вывода в отчет или интерфейс — нормально, для условий отбора строк — не годится.
DATETIME2 вместо DATETIME: что выбрать
Для нового кода в SQL Server 2008 и новее рекомендуется DATETIME2, а не DATETIME. Причина — точность и диапазон, а не сам факт «более новый тип»:
| Критерий | DATETIME | DATETIME2 |
|---|---|---|
| Диапазон дат | с 1753-01-01 | с 0001-01-01 |
| Точность времени | округление до .000/.003/.007 сек | до 100 нс, настраивается (n) |
| Минимальный размер | 8 байт всегда | 6 байт при низкой точности |
| Соответствие стандарту SQL | нет | да |
DATETIME округляет доли секунды до ближайших .000/.003/.007 — при сравнении значений, вычисленных разными способами, это дает неожиданные несовпадения. У DATETIME2 округления нет, а точность (число знаков после запятой у секунд) задается явно.
Единственная причина остаться на DATETIME — работа со старым приложением или ORM-слоем, который не умеет DATETIME2 или завязан на его диапазон. Для нового кода такого ограничения обычно нет.
Часовые пояса: DATETIMEOFFSET и AT TIME ZONE
DATETIMEOFFSET хранит момент времени плюс числовое смещение от UTC (например, +03:00) — это не то же самое, что часовой пояс с правилами перехода на летнее/зимнее время. Смещение фиксируется в момент записи и не пересчитывается автоматически.
Сменить смещение у уже сохраненного значения, не меняя сам момент времени в UTC, позволяет SWITCHOFFSET:
DECLARE @dto datetimeoffset = '2026-09-19 14:00:00 +03:00';
SELECT SWITCHOFFSET(@dto, '+00:00'); -- 2026-09-19 11:00:00 +00:00, тот же момент времени
Начиная с SQL Server 2016 есть AT TIME ZONE — переводит значение с учетом именованного часового пояса Windows ('Russian Standard Time' и подобные) и его текущих правил DST. Это удобнее ручной арифметики со смещениями, но опирается на таблицу часовых поясов ОС сервера: для дат в прошлом, когда правила перехода отличались от текущих, результат может быть неточным.
Выводы
- Для календарной даты без времени берите DATE (3 байта), для точного момента — DATETIME2 с нужной точностью, для значений с явным смещением от UTC — DATETIMEOFFSET.
- DATETIME2 предпочтительнее DATETIME для нового кода: больше диапазон, точнее время, без принудительного округления долей секунды.
- DATEDIFF считает пересеченные границы единицы измерения, а не точный интервал — при разнице в секунды результат в минутах/часах может быть больше реального.
- FORMAT() удобен для вывода, но не sargable и медленнее CONVERT — не используйте его в условиях WHERE.
- DATETIMEOFFSET хранит числовое смещение, а не правила часового пояса; для перевода с учетом летнего/зимнего времени нужен AT TIME ZONE (SQL Server 2016+).
Где применяется / связь с практикой
Освойте тему на практике
Правильный выбор типа даты и времени напрямую влияет на объем БД, точность отчетов и корректность работы с пользователями из разных часовых поясов — это база, с которой сталкивается любой T-SQL-разработчик уже на первых боевых задачах. Разобрать типы данных SQL Server глубже, потренироваться на реальных схемах и разобрать типичные ошибки с преподавателем можно на курсе MS SQL Server Developer.
Освойте тему на практике
Если пока не готовы к полному курсу — загляните на открытые уроки Otus: там разбирают конкретные темы SQL Server и других инструментов бесплатно, в прямом эфире с преподавателем.
FAQ
DATETIME хранится как два целых числа или как число с плавающей точкой?
Микрософт официально не документирует точный битовый формат хранения — это внутренняя деталь движка, на которую нельзя полагаться в коде. Для практики важны задокументированные диапазон, точность и размер из таблицы выше, а не внутреннее устройство.
Можно ли сравнивать значения DATETIME и DATETIME2 напрямую в запросе?
Да, SQL Server неявно приводит DATETIME к DATETIME2 при сравнении, но из-за разной точности и округления в DATETIME результат сравнения «на равенство» может отличаться от ожидаемого — надежнее явно привести оба значения через CONVERT/CAST к одному типу.
Что вернет GETDATE(), если сервер физически находится в другом часовом поясе, чем пользователь?
Локальное время самого сервера (или контейнера), без какой-либо привязки к поясу пользователя. Для многопользовательских приложений с разными поясами обычно хранят время в UTC (GETUTCDATE/SYSUTCDATETIME) и переводят в пояс пользователя уже на уровне приложения или через DATETIMEOFFSET.



