我过去曾在各种活动网站上工作过,不可避免地会出现一个不直观的困难问题,那就是存储活动发生时间的最佳方式。根据过去的经验,这是我目前的建议。
这是我几天前在 lobste.rs 上发布的一条评论的扩展版本,该评论最终在 Twitter 上引起了广泛关注。
问题
事件发生在某个日期、某个时间。那个时间的精确细节非常重要:如果您告诉人们晚上 7 点参加您的活动,结果他们应该在下午 6 点到达,他们就会错过一个小时的活动!
活动网站可能存在的一些最严重的错误是,人们一次前往一个地方,却发现他们来参加的活动没有在他们预期的时间发生。
那么如何存储事件的时间呢?
事实并非如此的“最佳实践”
每当您与数据库工程师讨论日期和时间时,您可能会得到相同的建议:将所有内容存储在 UTC 中。日期和时间非常复杂,唯一明确的存储方式是 UTC – 无需担心夏令时或时区,它记录了自宇宙诞生以来事件发生的确切时刻。
然后,当您向用户显示这些时间时,您可以将它们转换为该用户的当前时区 – 如今可以使用Intl.DateTimeFormat().resolvedOptions().timeZone浏览器 API 轻松使用。
这个建议有一个变体,您更有可能从 PostgreSQL 忠实者那里听到:使用TIMESTAMP WITH TIME ZONE或其方便的别名timestamptz
。这存储了 UTC 中的精确值,听起来它也可能存储时区……但事实并非如此! 存储的只是 UTC 值,该值是从插入值时激活或指定的任何时区转换而来的。
无论哪种情况,我们都会丢失有关该事件何时发生的关键信息。
可能出错的事情
计算事件开始的确切 UTC 时间并仅存储该时间有什么问题吗?
问题是我们正在丢失有关事件创建者最初意图的关键细节。
如果我安排明年 12 月 3 日下午 6 点举行晚间聚会,我的意思是当地时间下午 6 点,无论该特定日期的当地时间定义如何。
这次可能有多种方式最终会被误解:
- 用户错误:用户使用不正确的时区创建了活动
- 用户错误:用户在错误的位置创建了事件,稍后需要修复它
- 国际时区恶作剧:事件发生的地点在事件创建和事件发生之间的某个时刻改变了其时区规则
用户错误
到目前为止,这里最常见的问题是关于事件最初创建方式的用户错误。
也许您要求用户选择时区作为事件创建过程的一部分。这不是一个特别好的问题:大多数用户并不特别关心时区,或者可能不像专业软件开发人员那样理解和尊重时区。
如果他们选择了错误的时区,我们可能会向稍后查看其活动的其他人显示错误的时间。
我更关心的是位置。想象一下,用户在马萨诸塞州斯普林菲尔德创建了活动……然后几天后返回并将位置更正为伊利诺伊州斯普林菲尔德。
这意味着该事件发生在不同的时区。如果用户未能更新事件时间以匹配新位置,我们最终将在数据库中存储错误的时间。
国际时区恶作剧
我最喜欢的互联网角落之一是[email protected] 邮件列表。这是令人难以置信的开源tz 数据库的维护者们闲逛的地方,并跟踪时区规则的全球变化。
人们很容易低估这需要多少工作,以及这些规则变化有多么奇怪。这是最近的一封电子邮件,提出了一个全新的时区: Antarctica/Concordia
:
早上好。我写此信的目的是为全年开放的南极基地提议一个新的时区。该基地是法国-意大利联合研究设施,建于南极洲南极高原海拔 3,233 m(10,607 英尺)的 Dome C 位置。 https://en.wikipedia.org/wiki/Concordia_Station
时区为 UTC+8,无 DST。
这是一个非常简单的事情。这是 2023 年 3 月的一个更复杂的例子: 黎巴嫩 DST 更改内部存在争议:
黎巴嫩正在经历许多围绕推迟夏令时的最新决定的内部争议。许多机构拒绝遵守这一变化,并将于 3 月 26 日(星期日)采用常规夏令时。这些机构包括但不限于:
- 通讯社
- 宗教组织
- 学校、大学等…
拒绝的主要焦点是该决定的合法性,以及显然,由于通知时间太短而造成的技术混乱。此外,正如下面一些文章提到的,这也引起了宗派冲突。
黎巴嫩最终同时有多个时区处于活动状态,具体取决于您与哪个机构交谈!
令人惊讶的是,各国在很少通知的情况下做出有关夏令时的决定是很常见的。土耳其、俄罗斯、智利和摩洛哥是经常以这种方式给软件开发商造成短期混乱的另外四个例子。
如果您使用 UTC 存储活动开始时间,这将是一个大问题:新的 DST 规则意味着,根据您保存的 UTC 时间,在下午 6 点开始的现有活动现在可能会在当地时间下午 5 点或 7 点开始。存储在您的数据库中。
我的建议:存储用户的意图时间和位置/时区
我在这里强烈建议的是,记录的最重要的是原始用户的意图。如果他们说活动将于下午 6 点举行,请保存下来!确保当他们稍后编辑事件时,他们会看到与第一次创建事件时输入的相同的可编辑时间。
除此之外,尝试获取该事件发生所在时区的最准确的指示。
对于大多数活动,我认为最好的版本是场地本身的确切位置。
用户可能会发现时区令人困惑,但他们希望了解帮助与会者了解活动确切发生地点的重要性。
如果您知道场地位置,您几乎肯定可以从中得出时区。我说几乎是因为,就像任何涉及时间的事情一样,都会有边缘情况——最重要的是,对于正好位于一个时区与另一个时区的分界线上的场地来说。
我还没有坐下来为此设计理想的 UI,但我可以想象一些东西,让用户非常清楚事件在关键的本地规模上发生的确切地点和时间。
现在我们已经精确地捕获了用户的意图和事件位置(并通过它获取了确切的时区),我们可以进行反规范化:找出该事件的 UTC 时间并将其存储起来。
此 UTC 版本可用于各种目的:按时间对事件进行排序、弄清楚现在/接下来发生的情况、向其他用户显示事件并将其时间转换为当地时区。
但是当用户去编辑他们的事件时,我们可以准确地向他们展示他们最初告诉我们的内容。当用户编辑其事件的位置时,我们可以保留原始时间,并可能与用户确认他们是否想要根据新位置修改该时间。
如果地球上某个地方的立法机构对其 DST 规则做出了令人惊讶的更改,我们可以识别受该更改影响的所有事件,并相应地更新非规范化的 UTC 时间。
一般来说,时区 UI 很糟糕
顺便说一句,这是我在现代互联网上最不喜欢的与时间相关的 UI,来自 Google 日历:
连搜索选项都没有!祝你好运在那里找到 America/New_York,假设你知道这就是你首先要寻找的东西。
原文: https://simonwillison.net/2024/Nov/27/storing-times-for-human-events/#atom-everything