营业时间内的时差

Time difference within business hours

我想找出 NOW() 和时间戳列之间的时间差。 计算时差时,只需计算上午9点至下午5点的营业时间和工作日。

例如对于第一条记录2021-04-21 07:56:36,如果现在是2021-04-21 10:00:00,则必须从上午9点开始计算时差,在这种情况下,结果将是1小时,如下所示:

(NOW() - created_at) - ('2021-04-21 09:00'::timestamp - created_at) AS timediff

对于 2021-04-19 21:55:46 的第二条记录,结果将是从 2021-04-20 09:002021-04-20 17:002021-04-21 09:00 到 [=] 的 8+1 小时组成的 9 小时15=]

对于2021-04-17 14:22:05的第三条记录,它将是 17 小时,由 8+8+1 小时组成。

示例数据:

+----------------------------------+---------------------+
|                id                |     created_at      |
+----------------------------------+---------------------+
| 9d38ea6e7077400db310cfc47b31d482 | 2021-04-21 07:56:36 |
| 9d38ea6e7077400db310cfc47b31d481 | 2021-04-19 21:55:46 |
| 9d38ea6e7077400db310cfc47b31d480 | 2021-04-17 14:22:05 |
| 9d38ea6e7077400db310cfc47b31d479 | 2021-04-18 22:43:44 |
+----------------------------------+---------------------+

期望的结果:

+----------------------------------+---------------------+----------+
|                id                |     created_at      | timediff |
+----------------------------------+---------------------+----------+
| 9d38ea6e7077400db310cfc47b31d482 | 2021-04-21 07:56:36 | 01:00:00 |
| 9d38ea6e7077400db310cfc47b31d481 | 2021-04-19 21:55:46 | 09:00:00 |
| 9d38ea6e7077400db310cfc47b31d480 | 2021-04-17 14:22:05 | 17:00:00 |
| 9d38ea6e7077400db310cfc47b31d479 | 2021-04-18 22:43:44 | 17:00:00 |
+----------------------------------+---------------------+----------+

我建议生成 created_at 和 current_timestamp 之间的所有营业时间范围并将它们相加。虽然它在性能方面远非最佳解决方案,但我认为这是最直接的方法。 我使用时区范围为 tstzrange with intersection operator (*).

的时间戳
SELECT id,
  created_at,
  SUM(upper(business_range) - lower(business_range) ) business_hours
FROM (
  SELECT id, 
    created_at,
    tstzrange(date_in_range + '09:00'::time with time zone, date_in_range + '17:00'::time with time zone) 
      * tstzrange(created_at, current_timestamp) as business_range
  FROM (
    SELECT id,
      created_at,
      created_at::date + generate_series(0, current_timestamp::date - created_at::date) as date_in_range 
    FROM times
  ) dates_in_range
  WHERE date_part('dow', date_in_range) in (1,2,3,4,5)
) business_hour_ranges
GROUP BY
  id,
  created_at

请参阅 db<>fiddle

中的设置和示例

注意事项

虽然实现易于理解和调试,但可能会导致性能不佳 - 特别是如果使用非常旧的 created_at 日期。如果您的系统可能是这种情况,您最好编写函数来检查 created_at 和 current_date 的星期几,找到它们之间的天数并确定工作时间。