Как узнать количество дней в месяце?

Я пытаюсь получить следующее в Postgres:

select day_in_month(2);

Ожидаемый результат:

28

Есть ли какой-нибудь встроенный способ в Postgres сделать это?

Ответы

Ответ 1

SELECT  
    DATE_PART('days', 
        DATE_TRUNC('month', NOW()) 
        + '1 MONTH'::INTERVAL 
        - '1 DAY'::INTERVAL
    )

Замените NOW() любой другой датой.

Ответ 2

Использование умного "трюка" для извлечения части дня из последней даты месяца, как продемонстрировал Кассной. Но это может быть немного проще/быстрее:

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';

Все так же.

Ответ 3

Обратите внимание, что ожидаемый результат для 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

Ответ 4

Это также работает.

WITH date_ AS (SELECT your_date AS d)
SELECT d + INTERVAL '1 month' - d FROM date_;

Или просто:

SELECT your_date + INTERVAL '1 month' - your_date;

Эти два интервала возврата, а не целое число.

Ответ 5

Вы можете написать функцию:

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;

Ответ 6

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;