没有时区的时间戳中的夏令时时间偏移

Posted

技术标签:

【中文标题】没有时区的时间戳中的夏令时时间偏移【英文标题】:Daylight Savings Time shift in Timestamp without timezone 【发布时间】:2019-09-06 06:41:55 【问题描述】:

我正在使用 hibernate 与 postgresql 数据库进行通信,以存储用于建模的数据。在这些模型中,有基于没有时区的时间戳的时间序列。现在,当上传正确创建的时间戳时(在本例中为 2030 年),2030-03-31T02:00 的时间戳在数据库中的列中最终为 2030-03-31T-03:00没有时区的时间戳。我已经更改了数据库配置,使其时区为 UTC,但问题仍然存在。

问题似乎出在驱动程序 (https://mvnrepository.com/artifact/org.postgresql/postgresql/42.2.5) 或数据库中。将时间戳作为字符串插入时,它可以工作。这个问题也发生在本地和远程数据库上。

此外,休眠是在版本 5.4.2(最新)中,在我的 persistence.xml 中我设置了标志:

<property name="hibernate.jdbc.time_zone" value="UTC"/>

我写的一个小测试是这样的:

@Test
   public void testTimeShift() 

       EntityManager em = JPAUtil.createEntityManager();


       TimeStep timeStep = new TimeStep();
       LocalDateTime.now(Clock.systemUTC());
       LocalDateTime ldt = LocalDateTime.parse("2030-03-31T02:00:00");
       timeStep.setTimeStamp(ldt);

       SaveToDB.(timeStep, em);

       em.close();

       EntityManager em2 = JPAUtil.createEntityManager();

       TimeStep timeStep2 = LoadFromDB.getTimeStep(ldt, em2);

       if (timeStep.getTimeStamp() != timeStep2.getTimeStamp()) 
           System.out.println(timeStep.getTimeStamp());
           System.out.println(timeStep2.getTimeStamp());
           throw new EmptyStackException();
       
   

此代码返回以下内容:

2030-03-31T02:00
2030-03-31T03:00

Process finished with exit code -1

【问题讨论】:

如果您使用的是中欧时间(如欧洲/奥斯陆或欧洲/罗马),就 Java 而言,2030-03-31T02:00 不存在。它“认为”夏令时(DST)从那个时候开始,所以时间改为 03:00(规则可能会在 2030 年之前更改,但 Java 不知道)。我同意当您使用未指定的当地时间和 UTC 时,这无关紧要,但我敢打赌它仍然会发挥作用。 是的,我认为这在某种程度上是问题所在。但是当使用 log4j 输出记录器时,我可以看到休眠正在向驱动程序发送 02:00。所以我认为这在java方面似乎不是问题。另外,我在 2017 年的旧邮件列表中发现了类似的问题,但显然没有解决:postgresql.org/message-id/… 另外,我刚刚注意到,数据库中的时间步长是 2030-03-31T01:00。这让它更加陌生。总结一下:in: 2030-03-31T02:00 there: 2030-03-31T01:00 out: 2030-03-31T03:00 另一件事:如果我将本地系统时间设置为 UTC,那么即使对于远程数据库,一切都会按预期工作。现在这个解决方法没问题,但我仍然想知道为什么会发生这种情况 【参考方案1】:

答案是将时间戳的类型设置为 ZonedTimeData。为方便起见,可以从 LocalDateTime 解析它。这样,测试就成功了。

因此,与推荐的相反,对于不使用时区的时间步:

@Entity
@Table(name = "`TimeStep`")
public class TimeStep 

   @Column(name = "`TimeStamp`")
   private ZonedDateTime TimeStamp;

创建时间步然后通过

LocalDateTime.now(Clock.systemUTC());
LocalDateTime ldt = LocalDateTime.parse("2030-03-31T02:00:00");

ZonedDateTime zonedDateTime = ZonedDateTime.of(ldt, ZoneId.of("UTC"));
timeStep.setTimeStamp(zonedDateTime);

【讨论】:

以上是关于没有时区的时间戳中的夏令时时间偏移的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL 中的本地时区偏移量

时间戳中的 T 和 Z 到底是啥意思?

如何从java中的字符串时间戳中提取日期和时间

如何在 .NET 中的 DST 时间获取 UTC 偏移量

数据类型“带时区的时间戳”中的时区存储

从时区偏移或Angularjs中的日期获取时区[重复]