如何跳过一天而不是闰年

How to skip one day for not leap years

提问人:Dawid_K 提问时间:10/31/2023 最后编辑:Dawid_K 更新时间:11/2/2023 访问量:55

问:

我生成了一个表格,其中包含从 2000-01-01 到 2039-12-31(ISO 格式)的日期。日历包含闰年。现在,基于此,我想创建另一个具有一些转换的表。我有两列问题。

Fiscal_year_day_nr和。Fiscal_quarter_daynr

每年有 366 天,但不是闰年,则跳过 2 月 29 日。所以 2 月 28 日是 59 天,3 月 1 日是 61 天。对于闰年,2月29日有一个60号的一天。Fiscal_year_day_nr

我怎样才能在不闰年跳过那一天?假设该表包含一列,该列以所有日期命名,并基于此表创建新表,并使用 和 。calendardatedateFiscal_year_day_nrFiscal_quarter_daynr

我正在使用 Redshift SQL (RSQL)。

SQL PostgreSQL 日期 Amazon-Redshift

评论

0赞 Mike Organek 11/1/2023
我会将 、 和 组合在一起,并强制在闰年内完成计算,而不管该日期的实际年份如何。to_char()to_timestamp()datediff()
0赞 Zegarek 11/1/2023
出于某种原因,有人可能加倍努力将月份和日期加入到一个示例闰年,以产生“统一”的日编号。跳过那一天,保持编号的连续性,比引入这个差距更容易。如果你还没有,我希望在某个时候你会偶然发现一些遗留代码,这些代码希望你有这个差距。

答:

0赞 Zegarek 11/1/2023 #1
create table calendar as with recursive g("date") as 
  (select '2000-01-01'::date union all select "date"+1 from g 
   where "date" <= '2039-12-31'::date) 
select "date" from g;

为了保持连续编号,您可以使用 extract() 和 row_number() 窗口函数,窗口定义基于 date_trunc(): 演示

create table new_table as 
select "date", 
       extract(yearday from "date") as "Fiscal_year_day_nr",
       row_number()over(partition by date_trunc('year',"date"),
                                     date_trunc('quarter',"date")
                        order by "date") 
         as "Fiscal_quarter_daynr"
from calendar
order by "date";
日期 Fiscal_year_day_nr Fiscal_quarter_daynr
2023-12-31 (非闰年缩短) 365 92
2024-01-01 1 1
2024-02-28 59 59
2024-02-29 (闰年) 60 60
2024-03-01 61 61
2024-03-31 91 91
2024-04-01 92 1
2025-02-28 59 59
2025-03-01 (无间隙) 60 60
2025-03-31 90 90
2025-04-01 91 1

如果您确实希望看到非闰年的编号差距,则可以在适当的时候有条件地添加日期。转换条件以简化:booleanint

create table calendar_skipping_feb29 as 
select "date",
       extract(yearday from "date")
          +(extract(month from date_trunc('year',"date")::date+59) = 3
            and 60<=extract(yearday from "date"))::int
          AS "Fiscal_year_day_nr",
       row_number()over(partition by date_trunc('year',"date"),
                                     date_trunc('quarter',"date")
                        order by "date")
          +(extract(month from date_trunc('year',"date")::date+59) = 3
            and 60<=extract(yearday from "date") 
            and 1 =extract(quarter from "date"))::int
          AS "Fiscal_quarter_daynr"
from calendar;

如果一年中的第 60 天(1 月 1 日)在 3 月(月),则它是非闰年,因此从第 60 天开始,您将添加 to ( cast to is )。 做同样的事情,但仅限于第一季度。+5931"Fiscal_year_day_nr"trueint1"Fiscal_quarter_daynr"

日期 Fiscal_year_day_nr Fiscal_quarter_daynr
2023-12-31 (间隙增加的非闰年) 366 92
2024-01-01 1 1
2024-02-28 59 59
2024-02-29 (闰年不受影响) 60 60
2024-03-01 61 61
2024-03-31 91 91
2024-04-01 92 1
2025-02-28 59 59
2025-03-01 (间隙加) 61 61
2025-03-31 91 91
2025-04-01 92 1
-1赞 Belayer 11/1/2023 #2

优雅是美好的,但永远不要低估蛮力的力量。您只需使用标准 Postgres 提供的日期函数即可实现您正在寻找的日期函数,即使是版本 8(Redshift 的基础)和一些辅助函数。(见演示)

create or replace function is_leap_year(year_in integer)
  returns boolean
 language plpgsql
 immutable
as $$
declare 
    lvar date; 
begin
    lvar = make_date(year_in,02,29); 
    return true; 
exception 
    when others then
        return false;
end;
$$;

with std_calendar(gregorian_dt) as 
     ( select generate_series('2000-01-01'::date 
                             ,'2004-03-01'::date
                             ,interval '1 day'
                             )::date
      ) --select * from std_calendar
insert into fy_table(gre_date
                    ,fiscal_year_day_nr
                    ,fiscal_quarter
                    ,fiscal_quarter_daynr
                    )   
    select gregorian_dt::date
         , extract(doy from gregorian_dt)
               + case when not is_leap_year(extract(year from gregorian_dt)::integer)
                       and extract(month from gregorian_dt) > 2 
                      then 1
                      else 0
                 end fydn 
       , extract(quarter from gregorian_dt) fg 
       , extract( days from (gregorian_dt - date_trunc('quarter', gregorian_dt))) + 1
             + case when not is_leap_year(extract(year from gregorian_dt)::integer)
                     and extract(month from gregorian_dt) > 2 
                    then 1
                    else 0
               end fqdnr
  from std_calendar;

您没有定义您的会计年度,因此演示假设它与日历年相对应,并且您希望使用相同的跳过日功能。