如何根据 PL/pgSQL 中的开始日期和持续时间(工作日)获取结束日期?

Posted

技术标签:

【中文标题】如何根据 PL/pgSQL 中的开始日期和持续时间(工作日)获取结束日期?【英文标题】:How to get end date based on a start date and duration (workdays) in PL/pgSQL? 【发布时间】:2016-08-23 07:30:10 【问题描述】:

我需要根据开始日期和持续时间(代表工作日数的整数)计算工作的结束日期。

我已经编写了一个基于this answer 的函数,它计算两个日期(开始和虚拟结束)之间的周末天数

-- 0 Sunday
-- 1 Monday
-- 2 Tuesday
-- 3 Wednesday
-- 4 Thursday
-- 5 Friday
-- 6 Saturday
CREATE OR REPLACE FUNCTION weekend_days(date, date) RETURNS INT AS
$$
SELECT COUNT(days)::INT
    FROM generate_series($1, $2, '1 day') AS days
    WHERE EXTRACT(DOW FROM days) BETWEEN 1 AND 5;
$$
LANGUAGE 'sql' IMMUTABLE STRICT;

我想创建一个触发器 ON INSERT 或 ON UPDATE 来填充相应的 end_date 列。 显然,将周末天数添加到给定的持续时间并不能解决问题。

递归

有什么方法可以让递归函数继续添加周末或节假日?

编辑:也许可以在this answer 中找到另一个很好的例子,它混合了周末和节假日。

【问题讨论】:

使用日历表。您可能也关心假期,日历表是最安全的方法。 你能提供任何真实的例子吗?目前,我需要一个意大利语节假日日历 您需要自己创建。使用generate_series 生成日期。标记那些是假期等:) 最后我构建了一个帮助表来跳过假期,使用generate_seriesMeeus/Jones/Butcher 算法来计算未来几年的复活节星期日。 gist.github.com/sentenza/e8f8003e2581d52f12f8c89d663c00d7 【参考方案1】:
CREATE OR REPLACE FUNCTION is_business_day(p_date date)
  RETURNS boolean AS
$BODY$
DECLARE
    is_business_day boolean;
    dow int;
BEGIN
    dow := extract('dow' from p_date);

    IF dow = 6 OR dow = 0
        THEN
        is_business_day := FALSE;
    ELSE
        is_business_day := TRUE;
    END IF;

    RETURN is_business_day ;
END;
$BODY$
  LANGUAGE plpgsql IMMUTABLE;



CREATE OR REPLACE FUNCTION working_date(date start_date, int duration)
  RETURNS date AS
$BODY$
DECLARE 
    ret_date date;
    loop_date date;
BEGIN   
    --add days
    ret_date := start_date + (duration - 1);

    loop_date := start_date + 1; 
    --add extra day for each no business day between start_date and ret_date
    WHILE loop_date <= ret_date LOOP
        IF not is_business_day(loop_date) THEN
            ret_date := ret_date + 1;
        END IF;
        loop_date := loop_date + 1;
    END LOOP;
    --add day if ret_date is no business day
    WHILE not is_business_day(ret_date) LOOP
        ret_date := ret_date + 1;
    END LOOP;

    RETURN ret_date;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER
  COST 100;

【讨论】:

为什么is_business_day()函数里面有RETURN is_holiday?我想应该是RETURN is_business_day,对吧? 我编辑了第二个函数。没有返回指令,返回的类型必须是日期而不是整数。 我已经在gist 上报告了所有有用的 PgSQL。

以上是关于如何根据 PL/pgSQL 中的开始日期和持续时间(工作日)获取结束日期?的主要内容,如果未能解决你的问题,请参考以下文章

PL/pgSQL 中的 EXECUTE...USING 中忽略了 USING 部分

如何在 PL/pgSQL 中的动态选择查询中使用迭代器变量?

PL/pgSQL 从表中动态复制数据

PL/PgSQL:没有函数匹配给定的名称和参数类型。您可能需要添加显式类型转换

PostgreSQL 提高 PL/pgSQL 函数的性能

如何在 PL/pgSQL 中按行类型返回表