时间戳:iso8601与unix时间戳

26

我知道这是一个相当常见的问题,但我觉得我找到的答案并没有真正解决问题。我将概述我的具体用例,并呈现其他SO答案和网络信息的摘要。

对于我正在编写的服务,数据库条目在移动设备和我们的网站上都会创建和存储,并且需要进行双向同步。我们目前的目标是Android和iOS,它们都使用SQLite作为关系型数据库。服务器端使用Django和MySQL实现,但其他解决方案可能会在将来取代它。

根据这个SO答案中的想法,我们将实现同步: https://dev59.com/zW445IYBdhLWcg3wGmg6#5052208。对于同步,我们将使用时间戳,该时间戳仅由服务器设置并与客户端同步,用于最后同步日期和对象创建和更新的时间戳。由于对象引用用户在某些时间所做的事情,因此它们还具有时间戳信息。

我的问题只涉及用于这些时间戳的内部表示形式。 UI中的用户表示是内部表示的本地化和格式化版本。有很多不同的方式,无论在实现语言还是在使用的不同数据库中,都可以表示时间戳。经过相当多的研究,似乎只剩下两种有效的解决方案:

  • Unix时间戳
  • ISO8601

这个文章曾经在Hacker News上出现过(两次),建议使用Unix时间戳,并提出了一个非常好的论点。正如预期的那样,HN上的讨论在上述两个观点和其他方面都有所不同。

到目前为止,我的结论是Unix时间戳更容易处理,但似乎并不是Django中普遍采用的方法。我找到的几乎所有代码示例,从Django教程到许多其他网站,都在models.py中使用DateTimeField,该字段映射到SQL中的某种日期字段,具体术语取决于使用的数据库。

使用ISO8601日期进行传输和存储的缺点在于需要解析它们以创建相应实现语言的Date类型。这并不难,但有点烦人。对于我们使用的每种语言,您都需要一个(小)库或至少比您希望的更多的代码。它不是很漂亮,会创建依赖项,并且可能稍微慢一些。Unix时间戳在我所知道的任何语言中都没有这个问题。
另一个问题是,在数据库中(和解析期间)使用“智能”日期或时间戳字段可能会让您陷入麻烦。关于时间问题导致混乱的问题有很多。我的链接限制已达到,因此我无法发布任何内容,但您很容易找到一些类似的链接。
我们可以使用简化格式,不包含时区信息,并始终使用UTC。我们只会使用UTC,但似乎如果您使用ISO8601,则最好使用一个通用理解和明确的格式。Unix时间始终在UTC中,因此您永远不必担心这个问题。
当然,ISO8601具有在查看原始数据库时可读性强的优点,我也不必在2038年之前重写几行代码,但这似乎并不能弥补缺点。
通过将事情写出来,我实际上已经得到了答案。无论如何,我很想知道其他人的想法以及您在自己的项目中做了什么。请简要概述您的用例,以便其他人可以更好地对您的输入进行分类。
谢谢!
2个回答

2
在使用SQL时,你应该基本上总是使用timestamptz(带有时区的时间戳)而不是timestamp(不带时区的时间戳)。在你的后端中,使用默认类型(Py中的datetime,JS中的Date)。只要你这样做,就是安全的。在API中,ISO8601或Unix时间戳都是可行的选项。 timestamptimestamptz在你的表中存储的是完全相同的信息,即Unix时间戳。区别在于,timestamptz在转换为/从ISO字符串时包含时区信息,这是你的数据库驱动程序使用的。而timestamp不会像你期望的那样转换为UTC时间,而是保留在你的数据库区域设置的任何时区,并从结果中省略偏移量,从而产生歧义。你的客户端不知道要使用哪个时区,可能会假设一些不正确的东西,通常是UTC。
在Postgres中尝试这个例子:
-- Some systems default to UTC, some default to a local time zone. 
-- Let's see what happens in the latter case.
# SET timezone TO 'US/Pacific';

# SELECT now();  -- returns timestamptz
2023-09-29 09:26:40.277-07

# SELECT now()::timestamp;  -- Don't do this!
2023-09-29 09:26:43.927

# SET timezone TO 'UTC';

# SELECT now();
2023-09-29 16:27:10.32+00

顺便说一下,协调世界时(UTC)也是一个时区。和其他时区一样,ISO 1601规定你必须添加偏移量("+00")来指定它,否则会产生歧义。另外,"Z"和"+00"是相同的意思。

1
“关于时间数据类型或时间标准格式,无论它们是怎样的已知信息,都很难找到任何相关资料。过去几天里,我一直在努力选择正确的时间格式。 和你一样,我既在移动设备上编码,也在Web服务器上编码(更不用说还会有桌面应用程序)。
我主要在思考一个问题:“如何在移动设备、Web服务器(API或其他名称)和桌面应用程序之间实现无缝体验?如何确保事件发生的时间一致,并且确保该时刻在这个软件集合的不同层次中的表示相同?”
你可能知道,在移动设备上编码意味着你使用SQLite数据库,而Web应用程序则主要使用MySQL。我对得知从一个数据库转换到另一个数据库时时间数据类型是最大的头痛之一感到非常失望。该选哪种?DateTime?Timestamp?哦天啊,SQLite没有内置的时间数据类型...Unix时间?好的,没问题,当然MySQL不使用UNIX时间作为标准...那将太简单了…”
我学到的是...如果你想把数据放在时间轴上,并说这个事件发生在时间轴上的某个点,最好使用时间戳(无论是UNIX还是MySQL风格,即ISO8601)。对我来说,可读性只是一个细节。没有人应该阅读数据库表,计算机会,而你要告诉计算机按照人类可以理解的顺序处理数据。但是“时区是什么?”这个问题就出现了...好吧...那就去网上找答案吧。
我自己很惊讶MySQL不使用UNIX时间,我认为这是最标准和一致的时间格式。
我真的认为这些选择周围的文档和标准非常有限...我现在正在考虑写一些关于如何处理MySQL和SQLite时间的东西。我确实浪费了一半的时间在这个问题上,试图理解如何使它干净简单,结论是,凭借现有的文档,你就是做不到...
也许我错了...
无论如何,请看一下这个视频,展示了在MySQL中处理时间戳数据的困难:

http://www.youtube.com/watch?v=fp-etlirjbo


2
始终使用协调世界时 (UTC) 作为时区,并在需要本地时间时进行转换。 - Anthony Hunt
4
为什么?Unix时间戳是一个数字。*它的值始终为GMT *它要短得多(Unix时间戳只需要32位整数,而时间戳则需要200位ASCII码) *它是平台/语言无关的,并且可以被所有编程语言处理 *64位操作系统使用64位t_time整数,因此它将持续到宇宙的热死亡所以我真的很好奇,为什么你会选择为字符串解析而牺牲可移植性、大小和可塑性? - NullDivision
2
选择ISO-8601而不是UNIX时间戳的几个原因: 1)正确性:UNIX时间以众所周知的方式不计算偶尔发生的闰秒。ISO-8601时间戳可以在闰秒发生时正确表示这些为“23:60:00”的时间。 2)可逆性:ISO-8601时间戳可以包含时区/偏移数据,而UNIX时间戳则没有此功能。如果有使用情况需要知道原始事件发生的本地时间,则这一点很重要。由于UNIX时间戳不包含时区或偏移量,因此这是不可能的。 - Esa Lindqvist
@EsaLindqvist 关于闰秒的观点很有意思。然而,MySql和Postgres不支持闰秒。它们在内部使用Unix时间。从技术上讲,这意味着它们不使用协调世界时(UTC)! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接