如何在PostgreSQL中查找当前月份的第一个和第三个星期日/星期一

时间:2020-03-21 18:32:08

标签: postgresql

任何人都可以帮助我在Postgresql中查找每月的特定星期几吗……例如第一周和第三周的周日/星期一或第二周和第四周的周三

1 个答案:

答案 0 :(得分:1)

首先与日期相反的是复杂的,有时非常复杂。周数和周几的组合属于后一类。
问题源于2个ISO定义:

  • 所有星期从星期一开始,为期7天。
  • 一年的第一周是包含1月4日的一周。

这注定了失败的一切努力(至少是任何合理的简单尝试)。虽然令人钦佩,但我将使用@Abelisto建议作为示例。 See Fiddle。我已经将其更改为足以使用多个参数,虽然大多数月份是正确的,但请看2019年1月30日至31日和2021年1月。 当ISO周与日历完全一致时,第一个问题就不会出现。结果是给定月份的第一周与上个月的最后一周相同,反之亦然。 尽管通常可以单独解决此问题,但与其他解决方案结合使用时却不是这样。由于每个时间为7天,并且一年中的第1周包含4月1日,因此会引起更大的问题。 12月的最后几天可能在明年的第一周。同样,一月的第一天可以在上一年的52(或53)周中(请参阅小提琴中的第二个查询)。有解决方案吗?我确定那里有地方。我只是没有。至少具有提取功能。
那么这个特定的问题呢:基本上可以归结为上个月的最后一天,然后根据需要找到下一个DOW。现在来自Oracle后台,我只使用NEXT_DAY函数即可为我完成此任务。不幸的是Postgres没有提供有用的功能。但是你可以自己动手。下面,我提供了一些我在Postgres中编写的用于实现此功能的函数。它由2个Postgres SQL函数组成:
-utl_dates_first_dow_of_month()。它使用2个参数,目标星期几(DOW)作为日期名称的前3个字符(不区分大小写)和所需月份中的日期。它返回DATE,这是请求的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);
$$;

现在,解决当前问题的方法不再存在。正如Abelisto和其他人所指出的那样,请求是不明确的。没有第一个或第三个星期日/星期一这样的东西。您要每月的第一个和第三个星期日以及每月的第一个和第三个星期一吗?你想要 您是否要分别在每个月的第一个和第三个星期日以及每个星期一之后的星期一。您是否要在当月的第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;