В SQLite по сути нет типа даты
Это сбивает с толку всех, кто пришёл из Postgres или MySQL. В SQLite всего пять классов хранения — NULL, INTEGER, REAL, TEXT, BLOB, и больше ничего. Никаких DATE, DATETIME или TIMESTAMP. Да, в CREATE TABLE можно написать created_at DATETIME, и SQLite это проглотит, но под капотом значение всё равно ляжет как обычный текст или число.
Зато SQLite даёт набор функций, которые понимают три устоявшихся формата хранения даты:
- Текст в формате ISO 8601 —
'2026-04-23','2026-04-23 10:15:00','2026-04-23T10:15:00.123Z'. - Unix timestamp — количество секунд с 1970-01-01 UTC в виде целого числа.
- Юлианский день — дробное число дней с 4714 года до н. э., хранится как вещественное.
Выберите один формат и придерживайтесь его. ISO 8601 — самый читаемый вариант, к тому же такие строки корректно сортируются как обычный текст. Именно поэтому он считается форматом по умолчанию.
Четыре способа спросить «прямо сейчас» — текстовая дата, текстовая дата-время, секунды Unix и юлианский день. Все они описывают один и тот же момент времени.
Пять функций для работы с датами
В SQLite есть пять встроенных функций, которые покрывают практически любые задачи с датами:
date(time, ...)— возвращает дату в форматеYYYY-MM-DD.time(time, ...)— возвращает время в форматеHH:MM:SS.datetime(time, ...)— возвращаетYYYY-MM-DD HH:MM:SS.julianday(time, ...)— возвращает вещественное число (удобно для вычисления разницы между датами).strftime(format, time, ...)— возвращает строку в произвольном формате.
Первым аргументом каждая функция принимает значение времени, а дальше можно передать сколько угодно строк-модификаторов.
Обрати внимание на 'unixepoch' — это модификатор, которым мы говорим функциям работы с датой, что на входе у нас Unix-таймстамп, а не юлианский день. Без него SQLite будет считать число юлианским днём.
strftime в SQLite: свой формат даты
strftime — это рабочая лошадка. %-спецификаторы те же, что в C или Python:
Коды форматирования, которые понадобятся чаще всего:
%Y— год из четырёх цифр.%m— месяц (01–12).%d— день месяца (01–31).%H,%M,%S— часы, минуты, секунды.%w— день недели (0 = воскресенье).%j— день года (001–366).%s— Unix-время (timestamp).%f— доли секунды (SS.SSS).
Через strftime же удобно вытаскивать отдельные части даты — в SQLite нет отдельных функций вроде EXTRACT или YEAR(). Просто форматируете дату до нужного куска, а если требуется число — приводите тип:
strftime всегда возвращает текст, поэтому если нужно посчитать или сравнить как число — оборачивайте вызов в CAST(... AS INTEGER).
Модификаторы: арифметика дат без операторов
Именно эта возможность делает работу с датами в SQLite по-настоящему удобной. После аргумента со временем можно передать сколько угодно строк-модификаторов, и они применятся по порядку:
Модификаторы, которые пригодятся постоянно:
'+N days','-N days'и то же самое дляhours,minutes,seconds,months,years.'start of day','start of month','start of year'— округление вниз до соответствующей границы.'weekday N'— переход к ближайшему указанному дню недели (0 = воскресенье).'localtime'и'utc'— перевод между часовыми поясами.
Запомните трюк «последний день месяца»: начало месяца, плюс один месяц, минус один день. Отдельной функции LAST_DAY в SQLite нет, но цепочка модификаторов решает задачу ничуть не хуже.
UTC и локальное время
'now' всегда возвращает время в UTC. Если нужно локальное — это надо указать явно:
Модификатор 'localtime' переводит значение UTC в локальный часовой пояс системы. Модификатор 'utc' работает наоборот — принимает входное значение как локальное время и переводит его в UTC.
Хорошее правило: храните всё в UTC, а в локальное время переводите только при выводе. Если в хранилище смешаны часовые пояса, баги обязательно вылезут — причём дважды в год, когда переводят стрелки на летнее/зимнее время.
Сравнение дат и выборка по диапазону в SQLite
Если даты хранятся как текст в формате ISO 8601, то сравнение и BETWEEN работают сами собой — ISO 8601 сортируется лексикографически точно так же, как и хронологически. Собственно, ради этого его и сделали форматом по умолчанию.
Полуоткрытый интервал (>= начало, < конец) — отличная привычка: он сразу снимает вопрос «а полночь 30-го числа попала в выборку или нет?».
Для «последних 7 дней» пусть границу считает сам SQLite:
Разница между датами в SQLite
В SQLite нет функции DATEDIFF. Зато есть два приёма, которых хватает на все случаи жизни:
Разница, которую возвращает julianday(), измеряется в днях (с дробной частью), поэтому умножение на 24 даёт часы, а на 1440 — минуты. Разница strftime('%s', ...) считается в секундах — удобно, когда нужно целое число.
CAST(... AS INTEGER) отбрасывает дробную часть дней, если нужно получить количество полных суток:
Хранение даты в SQLite: выбираем один формат и не отступаем
Есть три разумных варианта — в порядке того, как часто их стоит выбирать:
- Текст в формате ISO 8601 (
TEXT). Читается в дампах глазами, корректно сортируется, дружит со всеми функциями работы с датой. Вариант по умолчанию. - Unix-время в секундах (
INTEGER). Компактно, сравнения работают быстро, никакой путаницы с часовыми поясами. Хороший выбор, когда строк миллионы. Чтобы прочитать значение обратно, понадобитсяdatetime(col, 'unixepoch'). - Юлианский день (
REAL). Редко себя оправдывает — разве что вы плотно занимаетесь арифметикой дат и хотите субсекундную точность в одном столбце.
Чего делать точно не стоит — так это мешать форматы в одном столбце. Функции работы с датой молча проглотят и то, и другое, а вот индексы, сортировка и сравнения начнут выдавать ерунду.
DEFAULT (datetime('now')) — это аналог DEFAULT CURRENT_TIMESTAMP в SQLite: каждая новая строка автоматически получает текущее время в UTC, и никакого кода на стороне приложения для этого не нужно.
Группировка по временным интервалам
Функция strftime особенно удобна, когда нужно разложить строки по месяцам, неделям или часам:
Та же логика подойдёт и для других задач: «заказы по часам», «регистрации по дням недели», «события по минутам» — берём формат, который даёт нужную детализацию, группируем по нему и считаем агрегат.
Дальше: агрегатные функции
Раз уж зашла речь о группировке — COUNT(*) это самая простая из агрегатных функций SQLite. Дальше разберём весь набор: SUM, AVG, MIN, MAX — и посмотрим, как они сворачивают множество строк в одно итоговое значение.
Часто задаваемые вопросы
Есть ли в SQLite тип DATE или DATETIME?
Нет, отдельного типа для даты в SQLite не предусмотрено. Дату можно хранить как TEXT в формате ISO 8601 ('2026-04-23 10:15:00'), как Unix timestamp в INTEGER или как юлианский день в REAL. Встроенные функции работы с датой принимают все три формата, а возвращают по умолчанию текст в ISO 8601.
Как получить текущую дату и время в SQLite?
Используйте date('now') для текущей даты, time('now') для времени, datetime('now') для даты и времени вместе, а strftime('%s', 'now') вернёт Unix timestamp. По умолчанию все эти функции отдают UTC — чтобы получить локальное время, добавьте модификатор 'localtime': datetime('now', 'localtime').
Как прибавить дни или месяцы к дате в SQLite?
Передайте в функцию строку-модификатор: date('2026-04-23', '+7 days'), date('now', '-1 month'), datetime('now', '+2 hours', '+30 minutes'). Модификаторы применяются последовательно, в порядке передачи. Поддерживаются единицы days, hours, minutes, seconds, months и years.
Как посчитать разницу между двумя датами?
Для разницы в днях используйте julianday(end) - julianday(start) — юлианские дни хранятся как REAL, поэтому в результате будут и дробные дни. Для секунд проще вычесть Unix timestamp: strftime('%s', end) - strftime('%s', start). Аналога DATEDIFF в SQLite нет, но этих двух приёмов хватает практически на все случаи.