我应该存储 UTC 时间戳还是本地时间以进行轮班

Should I store UTC timestamps or localtime for shifts

我正在开发一个函数,可以计算出有多少工人值班并打卡上班(或未打卡上班)。

工作人员 "clock in" 和 "out" 以及这些事件的时间戳将以 UTC 格式存储,因为这似乎是出现最多的建议。

但是班次从固定的本地时间开始,因此例如一个班次总是从 07:00 开始,而不考虑夏令时,并在例如 14:00.

结束

轮班工人 "allocated" 轮班。

第一个要求是能够知道有多少工人值班(打卡上班)"at this point in time" - 一种状态检查。

第二个要求是能够得到过去一天的报告,例如对于单个工人(该工人是否全班值班,是否迟到,他们是否迟到并要求加班待支付等)

所以不要假设所有事情总是只在一个时区内,我们的想法是将时区与班次定义一起存储在数据库中(班次记录可能包含本地开始时间、结束时间和本地时区 "name".)

问题一:本地时区存储推荐使用什么格式?有没有一种方法可以存储 start/end 轮班时间,让我可以编写一个 SQL 查询,将这些时间与提供的 UTC 时间进行比较。

问题二:还有什么建议吗?我是否应该将时钟 in/out 次存储为 Local 以与移位 start/end 次相同?或者我应该做一些魔术并将所有内容存储为 UTC 时间......但是如果它存储为“2:00 UTC”如何计算当前本地时间开始时间......将其转换为本地时间需要始终保持相同,例如 7:00 本地时间,无论夏令时如何....

首先,了解 SQL standard 几乎没有涉及日期时间。数据类型、定义和行为在各种数据库产品中 差异很大

你很幸运,Postgres has exceptionally rich handling of date-time data types and date-time functions。但是您必须仔细研究文档并进行实验,以便了解行为。

07:00, regardless of daylight savings

实际上,那不是不管,而是尊重 Daylight Saving Time (DST)。 DST 通过将 7 AM 推迟或提前一个小时来改变它的含义。 正确遵守夏令时是问题的核心。

为了其他读者,关于术语的一句话:问题上下文中的“本地时间”一词表示日期 and/or 不考虑时区的时间或 offset-from-UTC

因此,本地日期时间 不是 代表时间轴上的一个点,而是 可能 个点的粗略概念.在新西兰奥克兰,早上 7 点比法国巴黎早得多。反过来,法国巴黎的早上 7 点比加利福尼亚州蒙特利尔的早上 7 点要早得多。因此,如果没有时区,“本地”值就没有意义。在 Postgres 中,这些“本地”类型是:

  • timestamp without time zone(日期和时间)
  • date(仅限日期)
  • time without time zone(仅限时间)

时间轴上的实际时刻需要区域或偏移量。由于找不到更好的词,我们称这些为“分区”类型。在 Postgres 中是:

  • timestamp with time zone(日期和时间,调整为 UTC
  • time with time zone(时间,调整为 UTC)

了解 Postgres 从不 保存任何时区信息非常重要。尽管名称为“with time zone”,但时区不会保存为数据的一部分。相反,“with time zone”的意思是“with respect for time zone”,因为 Postgres 将输入值调整为 UTC

  • 对于分区类型,Postgres 将输入值中任何指定的 zone/offset 信息应用于 从 zone/offset 调整为 UTC。然后丢弃指定的 zone/offset 信息。
  • 相比之下,对于“本地”类型,任何指定的 zone/offset 信息都将被完全忽略

Question 1: What format is recommended for storing the local timezone?

因此,根据您的业务需求和规则,除了日期时间值之外,您可能还想单独记录 zone/offset。 Postgres 和 SQL 标准都没有指定区域或偏移量的数据类型。所以我建议存储为文本。

  • 与 UTC 的偏移量
    • 使用指定的格式 ISO 8601 standard for designators. These formats are primarily (a) Z for UTC (short for Zulu, means UTC) and (b) ±hh:mm where + means ahead of UTC (like India) and - (the MINUS character, or alternatively a HYPHEN-MINUS) 表示落后于 UTC(如美洲)。
    • 虽然 ISO 8601 允许省略小时的填充零和冒号,但我建议您永远不要这样做。许多协议和软件实现都需要这些位,没有它们可能会中断。
    • 示例:对于印度,比 UTC 早五个半小时,+05:30
  • 时区
    • 使用IANA在tzdata数据库中定义的continent/region格式的正式名称,以前称为Olson数据库。参见 this recent list of zones
    • 示例:对于印度,Asia/Kolkata

更多术语:与 UTC 的偏移量是指提前或落后于 UTC 的小时、分钟和秒数。时区是一个偏移量加上 一组处理异常的规则,例如夏令时 (DST)。因此,最好在知道时区的情况下使用时区。

回答问题:
您的解决方案需要“本地”和“分区”类型。

要记录班次的定义,您需要一个“本地”时间,time without time zone。当您记录班次通常应在早上 7 点开始时,您不希望 Postgres 更改或调整该值。

要记录特定班次的概念,您可以将 (a) 开始时间记录为 time without time zone,(b) 日期记录为 date.通过应用偏移量或区域,您可以确定 UTC 值。您可能想要 also/instead 记录 UTC 值本身。但要注意不要拖得太远,因为世界各地的政客都非常喜欢在几乎没有提前通知的情况下重新定义时区。

要记录作品实际打卡的时刻,您需要 UTC 时刻,timestamp with time zone 类型。如果需要,您可以输入分区值,Postgres 将调整为 UTC。如问题中所述,在时间轴上处理实际时刻时,几乎总是最好以 UTC 格式处理和存储数据。

对于这两种类型,您可能希望也可能不希望另外记录预期的时区。

Postgres 会话有默认时区。我建议你永远不要依赖它。最好始终在 SQL 代码 and/or 输入数据中指定预期时区。手动细读数据时,您可能会发现将会话默认设置为 UTC 或区域很方便,但我不会在代码中这样做。

Is there a way in which I can store the start/end times of shifts that allows me to write an SQL query that can compare those times with a supplied UTC time.

如上所述,您会将轮班的一般概念记录为“本地”。如“2015 年,杜塞尔多夫和底特律的工厂在早上 6 点开始,而德里因素在早上 7 点开始,但在 2016 年,所有三个工厂都在早上 7 点开始”。要记录任何一个实际班次,请以 UTC 记录,尽管您可能 想要记录“本地”值以供人类阅读。

Should I just store the clock in/out times as Local to be the same as the shift start/end times?

不,不,当然不是。时间线上的任何实际点、真实时刻都应以 UTC 记录。使用 timestamp with time zone 并让 Postgres 根据需要将输入调整为 UTC。虽然通常我建议您的应用程序事先使用 UTC 编程。

Or should I do some magic and store everything as UTC times

不需要魔法,只需一致地处理您的数据以始终包含时区(或偏移量)数据并调整为 UTC。在您的应用程序编程和数据库中直接了解这一点。例如,在 Java, hand off objects rather than mere strings for date-time values. With JDBC 4.2 the database can exchange OffsetDateTime (and, optionally, Instant or ZonedDateTime) objects. (Avoid the troublesome java.util.Date & .Calendar classes, now legacy.)

how to work out the current Local time start time of a shift if it is stored as "2:00 UTC"

如果您有 UTC 日期和时间,您始终可以应用时区(或偏移量)来查看“本地”值。

日期时间处理是一件棘手的事情。所以仔细想想,给自己时间去学习,练习,练习,再练习。提示:(a) 学习 24 小时制,(b) 在工作编程时,以 UTC 思考,在你的办公桌上放一个 UTC 时钟,忘记你自己的本地时区.主要考虑您自己的本地时区,并不断地与 UTC 来回转换,这会让您发疯并导致错误。