【问题标题】:Time handling in application handling multiple timezone应用程序处理多个时区的时间处理
【发布时间】:2021-12-28 00:36:05
【问题描述】:

我有来自多个时区的用户,并将时间以 UTC 格式存储在数据库中。 单独遇到时间部分的问题。

按如下方式存储一周的用户工作时间。

Id UserId Day StTime EndTime
1 1 0 10:00 18:00
2 1 1 10:00 18:00
3 1 2 10:00 18:00
4 1 3 10:00 18:00
5 1 4 10:00 18:00

星期,0 代表星期日,1 代表星期一,...

在这里,时间以 UTC 格式存储。时间正在根据浏览器时区在 UI 中进行转换。由于此处不需要日期部分,因此此处会引起问题。

例如,假设用户在 UI 中选择上午 7 点到下午 7 点,时间会转换为 UTC,即今天 15:00 到明天 02:00。因此,在 DB 中,时间被存储为

Id UserId Day StTime EndTime
1 1 0 15:00 02:00
2 1 1 15:00 02:00
3 1 2 15:00 02:00
4 1 3 15:00 02:00
5 1 4 15:00 02:00

这里,开始时间大于结束时间,这是不正确的。

仅存储时间部分时,我们需要如何处理时区?

我的要求是根据以下信息为用户获取开放槽。

  1. 预定时段
  2. 阻塞槽
  3. 工作时间

BookedSlots,

Id userId St_time(datetime) duration(mins)
1 1 2021-11-27 16:00:00 30
1 1 2021-11-27 18:00:00 45

这里的时间是 UTC。

  1. 被阻塞的插槽
Id userId St_time end_date st_time end_time
1 1 2021-11-22 2021-11-25 09:00 18:00
1 1 2021-12-17 2021-12-20 09:00 18:00

这里也需要以UTC格式存储时间

  1. 工作时间
Id UserId Day StTime EndTime
1 1 0 15:00 02:00
2 1 1 15:00 02:00
3 1 2 15:00 02:00
4 1 3 15:00 02:00
5 1 4 15:00 02:00

我需要根据以上 3 条信息计算空位。但是,当我们将其存储在 UTC 中时,时间部分会令人困惑。处理这个问题的最佳方法是什么?

【问题讨论】:

  • 你能进一步扩展吗?我不清楚实际问题是什么。 15:00列出的项目中没有开始时间;浏览器时间转换码在哪里?
  • @blurfus 更新了问题
  • “这里,开始时间大于结束时间,这是不正确的。” - 啊,但是你看,它是正确的!您使用的是 UTC,因此在 UTC 中,这些是正确的开始和结束时间。
  • 如果开始时间大于结束时间,您不能只添加一天吗?否则,我认为这会变得有点复杂,因为您将本质上是本地时间(并且在某种意义上不是特定日期)的工作时间与时区(特定日期!甚至不确定在这里使用 UTC 是否最好)。
  • 在这种情况下,您需要存储本地时间和时区。当地时间上午 7 点在 DST(夏令时)期间转换为与标准时间期间不同的 UTC 时间。对于使用夏令时的时区,但我想你不能排除某些用户在这样的时区生活和工作。

标签: datetime localization timezone utc timezone-offset


【解决方案1】:

避免午夜环绕的一种方法是将工作时间存储为具有开始时间和持续时间,而不是开始时间和结束时间,就像您为预订时段所做的那样。您可以对阻塞的插槽执行相同的操作,以便为所有事件提供一个很好的一致存储模型。

我还建议将工作时间存储在用户的本地时间中,并使用 IANA timezone string 指示工作地点。以 UTC 存储的问题是,某些时区将在一年中进入和退出夏令时。假设我在纽约市,我希望我的工作时间是纽约时间 09:00 到 17:00。由于此时是纽约的东部标准时间 (UTC-5),因此将转换为 14:00 至 22:00。一到夏天,纽约将进入东部夏令时间 (UTC-4),所以我的工作时间会突然出现在纽约时间 10:00 到 18:00。

您还可以将时区与用户信息一起存储,以便工作时间与时区无关。这将允许您指定诸如“用户所在时区的 09:00 到 17:00”之类的内容。

每当您处理重复性事件时,我总是发现将它们存储在事件发生的时区中会更清楚,因为从 UTC 转换的逻辑全年都在变化。对于绝对事件,例如预定和阻塞的时段,将它们保持在 UTC 仍然是有意义的,因为它们指的是一个特定的时间事件。

【讨论】:

    【解决方案2】:

    如果你想要结束时间 > 开始时间

    你可以做这种计算

    sTime = StTime
    eTime = StTime + mod(EndTime-StTime+24hour, 24)
    

    例如:

    StTime=15
    EndTime=02
    
    sTime = 15
    eTime = 15 + mod(2-15+24, 24) = 26
    

    其中 26 表示 (24+2) => 第二天 + 2 小时 和

    StTime=09
    EndTime=18
    
    sTime = 09
    eTime = 09 + mod(18-09+24, 24) = 18
    

    09 到 18 将保持不变

    在 Postgresql 中计算日期时间

    SELECT TO_TIMESTAMP('2021-11-26 00:00:00', 'YYYY-MM-DD HH24:MI:SS') +StTime AS stime , TO_TIMESTAMP('2021-11-26 00:00:00', 'YYYY-MM-DD HH24:MI:SS')+StTime+(MOD((EXTRACT(EPOCH FROM (EndTime - StTime)::interval)+86400)::int4, 86400) || ' second')::interval AS etime FROM WorkingHours
    

    你应该能够得到 end > start 的开始和结束日期时间

    结果示例

    UserId  StTime      EndTime       stime                   etime
    1       15:00:00    02:00:00      2021-11-26 15:00:00     2021-11-27 02:00:00
    2       09:00:00    18:00:00      2021-11-26 09:00:00     2021-11-26 18:00:00
    

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 2017-08-08
      • 2019-01-20
      • 1970-01-01
      • 1970-01-01
      • 2011-03-13
      • 1970-01-01
      • 1970-01-01
      • 2018-05-03
      • 2012-05-05
      相关资源
      最近更新 更多