在JSON中存储日期/时间的最佳方法是什么?

41

我刚接触JSON,在阅读JSON规范时,发现没有日期和时间的数据类型。经过一些研究,我发现有几种建议,其中之一是使用UNIX时间戳。这是最简单的方法吗?日后会遇到什么问题吗?


1
可能是“正确的”JSON日期格式的重复。 - Lokesh Dhakar
4个回答

61
我建议使用ISO 8601日期。特别是这种格式。
2014-03-12T13:37:27+00:00

可以跨越多种编程语言。

编辑:

JSON 只识别这些 类型

string
number
object
array
true
false
null

日期和日期时间最好以广泛使用的格式存储为字符串。

2
那么你只是把日期存储为字符串吗? - zgall1
1
@zgall1 那确实是最好的选择。 - Robin-Hoodie

6

我认为最好的答案取决于将来使用日期/时间的上下文。

简短版;

正确的格式取决于...

  • 数据使用的上下文(参见下面的示例)
  • 消费json的是人还是代码,以及您的编程语言或库是否轻松支持特定格式。

我建议使用json数字(Unix时间戳),然后根据用例结合用户时区或单独存储的“渲染时区”(json字符串)进行组合。这允许呈现最相关的日期/时间,并允许根据用户的位置/语言环境轻松使用不同的日期/时间格式。

或者,如果JSON可读性对您很重要,则可以使用ISO字符串格式,但仍要在代码中进行解析和时区转换,以获得渲染用户友好的日期/时间具有最大灵活性。

警告 如果您手动编写这些时间戳,请勿弄错。不同地区/日期的不同时区偏移量在一年中的不同日期更改,如果您针对给定区域/日期放置了错误的偏移量,则时间戳将无法表示正确的时间点...并且在解析和格式化它时,您可能会遇到显示错误时间的问题。

如果您希望JSON日期易于人类阅读

即,如果您希望人类能够直接阅读JSON并具有意义的日期/时间。

在这种情况下,@Ribtoks的答案可能是最好的。尽管如果日期/时间存储在与用户不同的时区中,则用户可能会感到困惑和/或需要自行执行时区转换以正确解释日期/时间。

更高的手动编辑诱惑和格式错误将导致解析错误。还可能出现UTC偏移量不正确(给定区域/日期的错误偏移量/时间)的情况,这将导致在格式化后向用户显示错误的日期/时间。

如果json只是数据存储,而日期/时间将由代码呈现

用户本地日期/时间格式

即,您希望以用户本地时区的方式向用户显示特定的时间点(日期/时间)。

在这种情况下,除了日期/时间数据表示特定时刻之外,您还需要一种方法来确定对于给定用户什么是“正确”的时区(不同的主题)。

您的代码将需要解析/转换JSON日期/时间数据,以便可以将其与用户的时区数据(例如America/DenverEurope/Berlin)组合以便以用户友好的方式进行打印格式。使用moment-timezone库进行此操作。

例如,时刻:
December 31, 2020 8:00 PM America/Denver

January 1, 2021 4:00 AM Europe/Berlin是相同的

这种行为通常在社交媒体、博客文章或留言板等地方很受欢迎,用户希望以他们自己的本地时间查看发布时间,而不关心作者所在的时区。
在这种情况下,由于我需要解析/格式化日期/时间,所以通常将日期/时间存储为unix时间戳(整数... json数字)。
将数字进行计算比解析包括内置时区的字符串时间戳更简单,以便了解基础特定时间点,然后将新的日期/时间字符串格式化为不同时区的用户。
活动/位置-本地日期/时间格式
即您想要向特定时区的用户显示特定的时间点(日期/时间)。
例如:柏林音乐会(面对面)的日期/时间。如果伦敦的听众购买门票,则显示伦敦时间演唱会开始的时间会令人困惑,因为他们将参加柏林的活动。
这与上面的“用户本地”案例相同,区别在于,您需要在单个特定的“事件位置时区”中呈现日期/时间,而不是为每个用户在不同的时区中格式化/呈现特定的时间点。
因此,除了存储特定的日期/时间外,还应存储要在其中呈现该日期/时间的时区。
虽然您可以将日期/时间预先格式化为字符串,但这会给您提供更少的选项以通过代码编程方式更改日期格式。例如,您可能希望使用活动本地时区,但对不同国家/地区的用户使用不同的日期/时间格式。
例如:柏林时间2020年11月25日晚上7点音乐会。相同的时间点,相同的时区,但不同的格式。
美国
11/25/20 7:00PM Europe/Berlin
德国
25.11.20 19:00 Europe/Berlin
英国
25/11/20 19:00 Europe/Berlin 所以在这种情况下,我还将日期/时间存储为json数字(Unix时间戳),然后将事件时区作为该时间戳旁边的字符串存储。这仍然使得解析/时区转换计算更简单。然后,您仍然需要确定用户特定的日期/时间格式首选项(浏览器区域设置、用户配置文件等...本问题范围之外)。
有关错误获取时区/偏移量的轶事
我有一个同事,在其计算机上设置了错误的时区(导致其位置的UTC偏移量错误)。
为了解决这个问题,他禁用了网络时间,并手动调整了计算机时钟以纠正错误的时区。
当他发送特定时间的会议邀请时,邀请中包括了时间和该时区的时区信息...这对于跨越时区边界进行电话会议时非常重要,因此每个人都能以当地时间查看会议并同时拨入。在我同事的情况下,当然是为不同的时区发送了这些会议邀请。
他非常抱怨每个人都错过了他的会议,并向我们展示了他安排在下午3点的会议,但我们所有人都收到了下午4点的邀请。
故事的寓意是...黑客行为通常最终会适得其反,因此确保您已存储了正确/预期的时间非常重要。您可以随时更改特定时间显示的时区/格式,但前提是您已经正确地存储了该时间。
获取底层时间通常在处理表单输入时发挥作用(例如,日期选择器提供YYYY-MM-DD,时间选择器提供HH:MM)。您需要根据用例确保用户的日期/时间输入字符串与时区上下文一起解释。

2
当然,你应该将字符串保存在JSON中。但是假设你想要过滤日期。你仍然可以将“数字”存储为字符串,例如“125”(二进制编码的十进制数),并且当你将其转换回125时它仍然有用。
话虽如此, 如果你想要自定义日期数字以进行过滤,例如: 使用长整型并对其执行按位操作。 https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
  • 保留12位YYYY直到4096-1年。
  • 保留4位MM,因为(0-11)足够了。
  • 保留5位DD,因为一个月的日期是(1-31)。

你可以继续保留位数,直到64位,包括小时和分钟等。在这种情况下,DD / MM / YYYY是21位。 通过使用这种方法,你同意@Clay Ferguson的观点,并且可以非常轻松地并且可能非常快速地按年、月、日过滤Map中的KeySet。


0

如果您更关心内存(和磁盘空间)的效率而不是原始JSON的人类可读性,最好将其存储为整数(即日期的毫秒值)。此外,如果您真的想要优化空间节省,可以将该整数编码为Base64,然后让JSON将Base64字符串作为JSON字符串保存。我甚至没有提到十六进制,因为如果您要编码为非十进制的内容,您可能会转向Base64。换句话说,与Base64相比,十六进制编码没有任何好处,因此只需使用Base64。


日期有时区,直接将其转换为毫秒值会丢失该特定日期的时区,使用像被接受的答案一样的ISO表示形式可以保留这个时区。 - Darryn Hosking
@Daxxy 嗯,你可以将时区与毫秒值一起存储。 - Bip901
2
@Daxxy 如果用户想要更改他们的时区,那么您需要解析他们的日期/时间字符串(包括旧时区),并使用新时区重新格式化字符串(即字符串中的时区和时间将更改)。如果您只是使用毫秒UTC,则始终可以轻松地在任何所需时区(用户偏好、管理员偏好等)呈现日期/时间,而无需每次解析字符串。 - mattpr
加上 +00:00 的时区表示并不是一个用户友好的时区,因为它是相对于 GMT 的固定偏移量,而许多用户都有夏令时,这意味着仅使用相对于 GMT 的固定偏移量只在一年中的一半有效。这就是为什么用户需要按地区指定时区(例如 America/DenverEurope/Amsterdam),然后渲染用户界面日期的软件需要使用 tzdata 表格以便找出该特定日期/地区的正确偏移量。如果您使用带有 +00:00 的字符串,则需要根据用户的 TZ 进行解析和重新渲染。 - mattpr

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