【问题标题】:How to get the difference in minutes between two timestamps excluding weekends?如何获得不包括周末的两个时间戳之间的分钟差?
【发布时间】:2021-10-16 20:58:43
【问题描述】:

我需要获取 postgres 中 2 个时间戳之间的分钟差异(不包括周末(周六、周日)),但我没有得到预期的结果。

示例:

在几分钟内获得差异,但是,包括周末

SELECT EXTRACT(EPOCH FROM (NOW() - '2021-08-01 08:00:00') / 60)::BIGINT as diff_in_minutes;

$ diff_in_minutes = 17566

获取工作日的差异,不包括周六和周日

SELECT COUNT(*) as diff_in_days
 FROM generate_series('2021-08-01 08:00:00', NOW(), interval '1d') d
 WHERE extract(isodow FROM d) < 6;

$ diff_in_days = 10

预期:

  • 从 '2021-08-12 08:00:00' 到 '2021-08-13 08:00:00' = 1440
  • 从 '2021-08-13 08:00:00' 到 '2021-08-16 08:00:00' = 1440
  • 从 '2021-08-13 08:00:00' 到 '2021-08-17 08:00:00' = 2880

等等……

【问题讨论】:

  • 如果timestamp 1或timestamp 2在周末会有什么结果?
  • 如果时间戳 1 是星期六,时间戳 2 是星期日,则应该为 0。如果时间戳 1 是 '2021-08-13 08:00:00' 而时间戳 2 是 '2021-08-16 08:00:00' 我希望在这些时间戳之间收到 1440(分钟差异)。

标签: postgresql date-arithmetic


【解决方案1】:

解决办法是:

SELECT GREATEST(COUNT(*) - 1, 0)
 FROM generate_series(from_ts, to_ts, interval'1 minute') AS x
WHERE extract(isodow FROM x) <= 5

所以

SELECT GREATEST(COUNT(*) - 1, 0)
 FROM generate_series('2021-08-13 08:00:00'::timestamp, '2021-08-17 08:00:00', '1 minute') AS x
WHERE extract(isodow FROM x) <= 5

返回 2880

【讨论】:

  • 如果两个时间戳在一个周末内,则固定返回 0
  • 我认为这是迄今为止最好的一个。您可以将其概括为一个可重复使用的函数,该函数也可以跳过非工作时间。
  • generate_series 使用的时间间隔越小(更精细)(例如,分钟而不是天) - 您将获得更差的性能。尤其是当您使用彼此相距太远的时间戳时。
【解决方案2】:

这不是一个最佳解决方案 - 但我会将找到最佳解决方案留给你作为家庭作业。

首先,创建一个 SQL 函数

CREATE OR REPLACE FUNCTION public.time_overlap (
  b_1 timestamptz,
  e_1 timestamptz,
  b_2 timestamptz,
  e_2 timestamptz
)
RETURNS interval AS
$body$
SELECT GREATEST(interval '0 second',e_1 - b_1 - GREATEST(interval '0 second',e_1 - e_2) - GREATEST(interval '0 second',b_2 - b_1));
$body$
LANGUAGE 'sql'
IMMUTABLE
RETURNS NULL ON NULL INPUT
SECURITY INVOKER
PARALLEL SAFE
COST 100;

然后,这样称呼它:

WITH frame AS (SELECT generate_series('2021-08-13 00:00:00', '2021-08-17 23:59:59', interval '1d') AS d)
SELECT SUM(EXTRACT(epoch FROM time_overlap('2021-08-13 08:00:00', '2021-08-17 08:00:00',d,d + interval '1 day'))/60) AS total
FROM  frame
WHERE extract(isodow FROM d) < 6

在 CTE 中,您应该向下舍入 2 个时间戳的左侧/较早的时间,并向上舍入 2 个时间戳的右侧/较晚的时间。这个想法是您应该在一整天内生成系列 - 而不是在一天的中间。

在调用 time_overlap 函数时,您应该使用 2 个时间戳的确切值,以便它正确计算生成的系列的每一天与您的 2 个时间戳之间的给定时间范围之间的重叠(以分钟为单位)。

最后,当您将所有重叠时间相加时 - 您将获得不包括周末的总分钟数。

【讨论】:

  • 我想知道time_overlap 是否已经可以作为时间戳范围的内置方法使用
  • @Bergi 有 OVERLAPS operator 但它返回布尔值而不是整数秒数。
  • 我宁愿想到两个tstzranges 上的* intersection operator - 但似乎将结果范围转换为interval(提取分钟)is not exactly simple(或作为内置)。
  • @Bergi 好吧,您可以简单地从范围的上限中减去范围的下限(交集运算符的结果是tstzrange),得到interval 类型的价值。您可以为此创建一个 SQL 函数,如 Wiki 中所述。
  • 是的,当然你可以这样做(正如我已经链接到的那样),但它确实需要创建一个我试图避免的 SQL 函数,只依赖内置函数。您也可以在查询中内联它,但它不再简单:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-14
相关资源
最近更新 更多