При работе с датами периодически приходится определять первый и последний день месяца по дате. Этим мы сейчас и займемся.

Для усечения даты в PostgreSQL есть функция date_trunc(поле, значение). Работает она аналогично функции trunc для чисел.

SELECT date_trunc('day', timestamp '2023-02-17 13:25')
# date_trunc
1 2023-02-17 00:00:00

Первый параметр функции date_trunc может принимать значения:

  • microseconds
  • milliseconds
  • second
  • minute
  • hour
  • day
  • week
  • month
  • quarter
  • year
  • decade
  • century
  • millennium

Первый день месяца

С помощью функции date_trunc мы можем легко определить первый день месяца:

SELECT date_trunc('month', timestamp '2023-02-17 13:25')
# date_trunc
1 2023-02-01 00:00:00

При желании можно привести результат к дате:

SELECT date_trunc('month', timestamp '2023-02-17 13:25')::date
# date_trunc
1 2023-02-01

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

SELECT (to_char(timestamp '2023-02-17 13:25', 'YYYY-MM') || '-01')::date
# date
1 2023-02-01

Последний день месяца

С последним днем месяца дела обстоят немножко сложнее. В месяце может быть от 28 до 31 дней в зависимости от месяца и года. Просто так подставить число в строку не получится.

На помощь нам придет получение первого числа месяца и добавление интервала в один месяц.

SELECT date_trunc('month', timestamp '2023-02-17 13:25') + interval '1 month' AS result
# result
1 2023-03-01 00:00:00

Так мы получаем первое число следующего месяца. Теперь нам достаточно отнять один день и мы получим последний день текущего месяца:

SELECT date_trunc('month', timestamp '2023-02-17 13:25') + interval '1 month -1 day' AS result
# result
1 2023-02-28 00:00:00

Итоговый запрос для получения дат первого и последнего дней месяца:

SELECT date_trunc('month', timestamp '2023-02-17 13:25') AS start,
       date_trunc('month', timestamp '2023-02-17 13:25') + interval '1 month -1 day' AS end
# start end
1 2023-02-01 00:00:00 2023-02-28 00:00:00

Добавление месяцев

Стоит отметить, как работает добавление месяцев к дате.

Если в исходной дате день месяца с 1 по 28, то в результате будет этот же день месяца.

SELECT timestamp '2023-02-17 13:25' + interval '1 month'
# ?column?
1 2023-03-17 13:25:00

Для дней с 29 по 31 может получиться так, что после добавления нужного количества месяцев этого же числа не будет в месяце результата. В этом случае будет взят последний день месяца.

Например, к 31 января добавляем 1 месяц. 31 февраля не существует, поэтому результатом будет последний день февраля - 28 февраля (или 29 для високосного года).

SELECT date '2023-01-31' + interval '1 month'
# ?column?
1 2023-02-28 00:00:00

Для високосного года:

SELECT date '2024-01-31' + interval '1 month'
# ?column?
1 2024-02-29 00:00:00

Определение начала и конца года

Аналогично определению первого и последнего дня месяца можно найти первый и последний день года по дате.

SELECT date_trunc('year', timestamp '2023-02-17 13:25') AS start,
       date_trunc('year', timestamp '2023-02-17 13:25') + interval '1 year -1 day' AS end
# start end
1 2023-01-01 00:00:00 2023-12-31 00:00:00

9.11 Динамическое построение интервала

9.13 EXTRACT - извлечение из даты части (год, месяц, день...)