Во-первых, вопреки первоначальным ожиданиям, работа с датами сложна, иногда чрезвычайно. Комбинация номеров недели и дней недели относятся к последней категории. Проблема связана с двумя определениями ISO:
- Все недели начинаются с понедельника и длятся 7 дней.
- Первая неделя года - это неделя, содержащая 4 января.
Это обрекает любые усилия (по крайней мере, на любое простое на c) на провал. В то время как замечательное усилие я буду использовать предложение @Abelisto в качестве образца. См. Скрипку . Я изменил это достаточно, чтобы использовать несколько параметров, хотя в большинстве месяцев это правильно, но посмотрите на 30,31-январь-2019 и январь-2021. Проблема с первым существом, когда неделя ИСО совершенно соответствует календарю, нет. Это приводит к тому, что первая неделя данного месяца совпадает с последней неделей предыдущего месяца, и наоборот. Хотя обычно это можно обойти самостоятельно, но не в сочетании с другими. В результате, каждый из которых длится 7 дней, а первая неделя года, содержащая 4 января, порождает большую проблему. Последние несколько дней De c возможно на 1-й неделе следующего года. Также первые дни января могут быть на 52 (или 53) неделе предыдущего года (см. 2-й запрос в скрипке). Есть ли решение? Я уверен, что где-то там есть. У меня просто этого нет. По крайней мере, с функцией извлечения. Итак, как насчет этой конкретной проблемы: в общем, все сводится к получению последнего дня предыдущего месяца, а затем к поиску следующего DOW (воскресенье или понедельник) по мере необходимости. Теперь, исходя из Oracle фона, я бы просто использовал функцию NEXT_DAY, которая бы сделала это для меня. К сожалению, Postgres не предоставляет эту полезную функцию. Но вы можете свернуть свои собственные. Ниже я приведу пару функций, которые я написал для выполнения этой функции в Postgres. Он состоит из 2 Postgres SQL функций: - utl_dates_first_dow_of_month (). Он принимает 2 параметра: целевой день недели (DOW) в качестве первых 3 символов названия дня (без учета регистра) и дату в желаемом месяце. Он возвращает ДАТУ, которая является первым вхождением запрошенного DOW.
- utl_dates_next_dow (). Он принимает те же 2 параметра и возвращает следующую календарную дату указанной DOW от указанной даты. Если указанная дата попадает в запрошенный DOW, процедура НЕ возвращает указанную дату. Функция фактически используется первой. К счастью, подпрограммы короче, чем описание.
create or replace function utl_dates_next_dow(dow_in text, date_in date)
returns date
language sql
immutable strict
as $$
-- Given a DOW and a date return the calendar date for the next occurrence of DOW
with dy as (select string_to_array('mon,tue,wed,thu,fri,sat,sun', ',') dl)
, dn as (select array_position(dl, (substring(to_char(date_in, 'day'),1,3))) fn
, array_position(dl, lower(substring(dow_in,1,3))) dn
from dy
)
select case when dn <= fn
then (date_in + (dn+7-fn) * interval '1 day')::date
else (date_in + (dn-fn) * interval '1 day')::date
end
from dn;
$$;
create or replace function utl_dates_first_dow_of_month(dow_in text, date_in date)
returns date
language sql
immutable strict
as $$
-- Given a DOW and a Date return the calendar date of the first specified dow in which the specified date falls.
select utl_dates_next_dow(dow_in, (date_trunc('month', date_in) - interval '1 day')::date);
$$;
Теперь с этим по пути к решению данной проблемы. Как Абелисто, так и другие указывают на просьбу неоднозначно. Нет такого понятия, как 1-е или 3-е воскресенье / понедельник. Вы хотите 1-е и 3-е воскресенье и 1-й и 3-й понедельник месяца? Хотите ли вы хотите 1-е и 3-е воскресенье месяца и понедельник, следующий за каждым соответственно. Хотите ли вы воскресенье и понедельник для 1-й и 3-й недели месяца (если это так, понедельник всегда будет более ранней датой, см. Определение 1 выше)? Пожалуйста, постарайтесь быть более точным с вашими вопросами. И включите данные испытаний - в виде текста без изображений - и ожидаемые результаты из этих данных. Решения, однако, являются лишь незначительными изменениями друг друга. (Нет решения для 3-й перечисленной возможности.)
Для случая 1-го и 3-го воскресенья и 1-го и 3-го понедельника:
with parms (dt) as (values ( date '2020-04-01'), (date '2020-06-01') )
, base_dates( fsun, fmon) as
( select utl_dates_first_dow_of_month('Sun',dt)
, utl_dates_first_dow_of_month('Mon',dt)
from parms
)
select '1st & 3rd Sunday and 1st & 3rd Monday'
, fsun "1st Sunday"
, (fsun+interval '14 days')::date "3rd Sunday"
, fmon "1st Monday"
, (fmon+interval '14 days')::date "3rd Monday"
from base_dates;
Для 1-го и 3-го воскресенья месяца и понедельник следующий:
with parms (dt) as (values ( date '2020-04-01'), (date '2020-06-01') )
, base_dates( fsun, fmon) as
( select utl_dates_first_dow_of_month('Sun',dt)
, (utl_dates_first_dow_of_month('Sun',dt)+interval '1 day')::date
from parms
)
select '1st & 3rd Sunday and Monday Following '
, fsun "1st Sunday"
, fmon "1st Monday"
, (fsun+interval '14 days')::date "3rd Sunday"
, (fmon+interval '14 days')::date "3rd Monday"
from base_dates;