我有一台正在从不同时区的客户端馈送数据的服务器。数据提要包含人员,他们的出生日期和其他事件日期。就我们的目的而言,如果我们只存储日期给我们的日期,那将很方便。

例如,如果客户在加利福尼亚州,并且告诉我们该人的出生日期是5月31日,我们希望将其存储在数据库中太平洋时间1999年5月31日。这样,无论您处于哪个时区,都可以看到此人于5月31日出生。

同时,我们希望能够查询此数据,以弄清"此人是未成年人吗"或"此事件是否发生在24小时之前?

客户端通过基于http的rest API向我们发送数据。服务器用Java编写(使用eclipselink)。该数据库是postgresql。是否可以满足这些要求?

通常,人们会说将所有内容都存储为UTC,但是我觉得那是个坏主意,因为我们会丢失原始数据的时区。

UTC是必经之路。对于timestamptz(timestamp with time zone),输入值的时区仅用作Postgres的修饰符,以在内部计算UTC。 (对于timestamp [without time zone],任何附加的时区都将被忽略!)。时区未保存。您需要另外保存它,以了解世界上发生了什么事。

如果这样做,则最好存储本地时间戳。只是不要混淆哪个是哪个。要么将所有内容都转换为UTC(timestamptz会自动发生),要么将所有内容都转换为本地时间(定义"本地":您的本地?是db服务器本地的?是用户本地的?)。

特别是,应存储确切的时区名称(或对其的引用),而不是仅存储"太平洋时间"。这对于夏令时,leap秒或其他事件更准确。

详细说明:

在Rails和PostgreSQL中完全忽略时区

关于时区名称和缩写:

具有相同属性的时区名称应用于时间戳时会产生不同的结果

关于时区处理:

选择计划的项目时在Postgres中考虑DST

在PostgreSQL时间戳类型中保留时区

因此,您说每个日期列也需要一个额外的original_timezone列吗?

是。如果要同时回答两个问题:birthday of person和did this event happen less than 24 hours ago?,则需要时间和空间("生日"取决于发生的位置)。实际上,我建议使用时间戳记(timestamptz或timestamp类型),而不只是date。

令人困惑的是,java.util.Date存储时间数据。我说那是指Java日期。谢谢

与时区和时区相关的混乱很多。此外,我至少要提到一次,DST(夏令时)是一个非常愚蠢的概念,应该消除并且再也不会被提及。

答案正确地描述了在UTC中存储主要日期时间值以及在UTC中执行大多数业务逻辑的最佳实践。然后,仅当用户在用户界面中期望时,才转换为本地化的日期时间。其次,如果知道原始输入的日期-时间和时区信息很重要,则除了记录主要UTC值外,还要记录下来。如"答案"所述,TIMESTAMP WITH TIME ZONE的意思是"在插入和检索日期时间值时相对于时区",但实际上存储了调整为UTC的日期时间而不存储原始时区信息。

仔细阅读有关日期时间数据类型的Postgres文档。并阅读由Postgres专家David E. Wheeler撰写的总结摘要建议,始终使用带时区的TIMESTAMP。

Erwin Brandstetter的回答是100%正确的。

计算年龄

至于诸如计算未成年人的年龄之类的问题,由于一天中的时间而有些棘手。使用Joda-Time库,可以调用方法withTimeAtStartOfDay将DateTime对象设置为一天的第一时刻。通常,第一时刻是时间00:00:00,但并非总是由于夏时制或其他异常。忽略与"午夜"相关的类和方法,因为它们已被上述方法所取代。

此外,要真正准确地确定年龄以合法地覆盖自己,您可能希望将年龄计算为出生日期时间之后的一两天的第一时刻。除非您知道他们的出生时间和该出生的时区,否则您将无法确切知道他们的年龄。

避免使用j.u.Date/.Calendar

众所周知,与Java捆绑在一起的java.util.Date和.Calendar类很麻烦。避免他们。使用Java 8中捆绑的Joda-Time和/或新的java.time包(受Joda-Time启发,但已重新构造)。

与java.util.Date不同,其他两个库中的日期时间对象都知道它们自己分配的时区。 j.u.Date尤其令人困惑,因为它没有分配时区,但它的toString方法应用了JVM当前的默认时区,从而产生了分配时区的错觉。

Joda-Time | java.time

使用Joda-Time和java.time,事情变得更加清晰。您可以为每个日期时间对象指定一个时区(否则将分配JVM默认值)。您可以轻松地从一个时区转换为另一时区。

这两个库都使用不可变对象,在不可变对象的基础上,将创建一个基于原始对象的新对象,而不是更改(变异)原始对象。

如果您认为重要,可以调用getZone Joda-Time DateTime对象获取其时区名称(ID)及其与UTC的偏移量以备记录。

ISO 8601

了解有关日期时间值的明智String格式的ISO 8601标准。考虑在基于文本的API中使用它们。现在,ISO 8601是所有新Internet协议的规范。例如:2014-08-13T16:02:01Z或2014-12-22T11:54:23+04:00。

并使用正确的时区名称。避免使用3或4个字母代码,因为它们既不是标准化的也不是唯一的。