You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中如何正确计算工作日与全天候时间间隔?

问题分析与解决方案

问题原因

你计算bus_interval时出现多1天的错误,根源在于工作日计数和间隔计算的逻辑错误:

  • 你的bus_days通过generate_series生成从起始日期到结束日期的所有日期并统计工作日,比如FirstLineSupport的起始日2023-05-23(周二)到结束日2023-05-26(周五),会统计到4个工作日(23、24、25、26号)。
  • 后续用bus_days -1生成天数间隔,再加上时间差部分,相当于把首尾两个部分天数的日期算成了完整的1天额外天数,导致最终结果多了1天。

正确计算方法

方法1:通过总间隔减去周末时长(简洁高效)

核心逻辑是先算出全天候总间隔,再减去时间段内包含的周六、周日的总时长(每天24小时),得到工作日间隔:

SELECT
    t.entity_id,
    t.type_de_demande,
    t.phase,
    CASE WHEN ts.end_ts IS NULL THEN NULL
         ELSE (ts.end_ts - ts.start_ts) 
              - (COUNT(*) FILTER (WHERE EXTRACT(ISODOW FROM gs.day) IN (6,7)) * INTERVAL '1 day')
    END AS bus_interval,
    ts.end_ts - ts.start_ts AS interval_24x7,
    ts.start_ts,
    ts.end_ts,
    t.time,
    t.next_time
FROM requests AS t
LEFT JOIN LATERAL (
    SELECT 
        to_timestamp(t.time / 1000)::timestamp AS start_ts,
        to_timestamp(t.next_time / 1000)::timestamp AS end_ts
) AS ts ON true
LEFT JOIN LATERAL generate_series(
    ts.start_ts::DATE, 
    ts.end_ts::DATE, 
    INTERVAL '1 day'
) AS gs(day) ON true
GROUP BY t.entity_id, t.type_de_demande, t.phase, ts.start_ts, ts.end_ts, t.time, t.next_time
ORDER BY t.phase;

方法2:拆分首尾部分天数+中间完整工作日(精确可控)

如果需要更精细的时间拆分计算,可以单独处理首尾工作日的部分时长,加上中间完整工作日的总时长:

SELECT
    t.entity_id,
    t.type_de_demande,
    t.phase,
    CASE 
        WHEN ts.end_ts IS NULL THEN NULL
        ELSE (
            -- 起始日的有效时长(仅当起始日是工作日)
            CASE WHEN EXTRACT(ISODOW FROM ts.start_ts) < 6 
                 THEN LEAST(ts.start_ts::DATE + INTERVAL '1 day', ts.end_ts) - ts.start_ts
                 ELSE INTERVAL '0' END
            +
            -- 结束日的有效时长(仅当结束日是工作日)
            CASE WHEN EXTRACT(ISODOW FROM ts.end_ts) < 6
                 THEN ts.end_ts - GREATEST(ts.end_ts::DATE, ts.start_ts)
                 ELSE INTERVAL '0' END
            +
            -- 中间完整工作日的总时长
            (COUNT(*) FILTER (WHERE gs.day BETWEEN ts.start_ts::DATE + INTERVAL '1 day' AND ts.end_ts::DATE - INTERVAL '1 day') * INTERVAL '1 day')
        )
    END AS bus_interval,
    ts.end_ts - ts.start_ts AS interval_24x7,
    ts.start_ts,
    ts.end_ts,
    t.time,
    t.next_time
FROM requests AS t
LEFT JOIN LATERAL (
    SELECT 
        to_timestamp(t.time / 1000)::timestamp AS start_ts,
        to_timestamp(t.next_time / 1000)::timestamp AS end_ts
) AS ts ON true
LEFT JOIN LATERAL generate_series(
    ts.start_ts::DATE, 
    ts.end_ts::DATE, 
    INTERVAL '1 day'
) AS gs(day) ON EXTRACT(ISODOW FROM gs.day) < 6
GROUP BY t.entity_id, t.type_de_demande, t.phase, ts.start_ts, ts.end_ts, t.time, t.next_time
ORDER BY t.phase;

两种方法都能得到正确的FirstLineSupport工作日间隔:{"days":2,"hours":22,"minutes":26,"seconds":43},与全天候间隔的日期部分一致。

内容的提问来源于stack exchange,提问作者executable

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:37:23