Ответ 1
SELECT
DATE_PART('days',
DATE_TRUNC('month', NOW())
+ '1 MONTH'::INTERVAL
- '1 DAY'::INTERVAL
)
Замените NOW()
любой другой датой.
Я пытаюсь получить следующее в Postgres:
select day_in_month(2);
Ожидаемый результат:
28
Есть ли какой-нибудь встроенный способ в Postgres сделать это?
SELECT
DATE_PART('days',
DATE_TRUNC('month', NOW())
+ '1 MONTH'::INTERVAL
- '1 DAY'::INTERVAL
)
Замените NOW()
любой другой датой.
Использование умного "трюка" для извлечения части дня из последней даты месяца, как продемонстрировал Кассной. Но это может быть немного проще/быстрее:
SELECT extract(days FROM date_trunc('month', now()) + interval '1 month - 1 day');
extract
является стандартным SQL, поэтому может быть предпочтительнее, но внутренне разрешает ту же функцию, что и date_part()
. Руководство:
Функция
date_part
смоделирована на традиционном Ingres, эквивалентном стандартной функцииextract
SQL:
Но нам нужно только добавить один interval
. Postgres позволяет использовать несколько единиц времени одновременно. Руководство:
значения
interval
могут быть записаны с использованием следующего подробного синтаксиса:
[@]
quantity unit
[
quantity unit
...] [
direction
]
где
quantity
- это число (возможно, подписанное);unit
-microsecond
,millisecond
,second
,minute
,hour
,day
,week
,month
,year
,decade
,century
,millennium
или сокращения или множественные числа этих единиц;
ISO 8601 или стандартный формат SQL также принимаются. В любом случае, руководство снова:
Внутренние
interval
значения хранятся в виде месяцев, дней и секунд. Это сделано потому, что число дней в месяце варьируется, и день может иметь 23 или 25 часов, если речь идет о переходе на летнее время. Поля месяца и дня являются целыми числами, а поле секунд может хранить дроби.
(Вывод/отображение зависит от настройки IntervalStyle
.)
В приведенном выше примере используется формат Postgres по умолчанию: interval '1 month - 1 day'
. Они также действительны (хотя и менее читабельны):
interval '1 mon - 1 d' -- unambiguous abbreviations of time units are allowed
Формат IS0 8601:
interval '0-1 -1 0:0'
Стандартный формат SQL:
interval 'P1M-1D';
Все так же.
Обратите внимание, что ожидаемый результат для day_in_month (2) может быть 29 из-за високосных годов. Возможно, вы захотите передать дату вместо int.
Кроме того, остерегайтесь перехода на летнее время: удалите часовую зону, или некоторые вычисления могут быть неправильными (следующий пример в CET/CEST):
SELECT DATE_TRUNC('month', '2016-03-12'::timestamptz) + '1 MONTH'::INTERVAL
- DATE_TRUNC('month', '2016-03-12'::timestamptz) ;
------------------
30 days 23:00:00
SELECT DATE_TRUNC('month', '2016-03-12'::timestamp) + '1 MONTH'::INTERVAL
- DATE_TRUNC('month', '2016-03-12'::timestamp) ;
----------
31 days
Это также работает.
WITH date_ AS (SELECT your_date AS d)
SELECT d + INTERVAL '1 month' - d FROM date_;
Или просто:
SELECT your_date + INTERVAL '1 month' - your_date;
Эти два интервала возврата, а не целое число.
Вы можете написать функцию:
CREATE OR REPLACE FUNCTION get_total_days_in_month(timestamp)
RETURNS decimal
IMMUTABLE
AS $$
select cast(datediff(day, date_trunc('mon', $1), last_day($1) + 1) as decimal)
$$ LANGUAGE sql;
SELECT cnt_dayofmonth(2016, 2); -- 29
create or replace function cnt_dayofmonth(_year int, _month int)
returns int2 as
$BODY$
-- ZU 2017.09.15, returns the count of days in mounth, inputs are year and month
declare
datetime_start date := ('01.01.'||_year::char(4))::date;
datetime_month date := ('01.'||_month||'.'||_year)::date;
cnt int2;
begin
select extract(day from (select (datetime_month + INTERVAL '1 month -1 day'))) into cnt;
return cnt;
end;
$BODY$
language plpgsql;