HTC和UTC时区问题的Spring Boot存储日期问题

时间:2018-09-17 08:38:52

标签: java spring hibernate datetime spring-boot

我正在做一些测试,以将UTC定义为应用程序的默认时区。首先,我希望将datetime值与UTC一起存储。

根据VLAD MIHALCEA(in another constructor)和https://vladmihalcea.com/how-to-store-date-time-and-timestamps-in-utc-time-zone-with-jdbc-and-hibernate/,我已经在属性文件中进行了设置:

spring.jpa.properties.hibernate.jdbc.time_zone= UTC

为了测试我正在使用h2数据库,我创建了一个具有所有java 8 dateTime类型的示例实体。

在我的liquibase配置中,它们的定义如下:

<column name="instant" type="timestamp"/>
<column name="local_date" type="date"/>
<column name="local_time" type="time"/>
<column name="offset_time" type="time"/>
<column name="local_date_time" type="timestamp"/>
<column name="offset_date_time" type="timestamp"/>
<column name="zoned_date_time" type="timestamp"/>

我认为我在每个字段中都使用好类型。它适用于除“ local_time”,“ offset_time”以外的所有字段,这些字段是Time sql类型而不是timestamp。

https://moelholm.com/2016/11/09/spring-boot-controlling-timezones-with-hibernate/

您可以看到我在上午8:39(巴黎GMT + 2)添加了此行,并且时间戳具有良好的UTC值(上午6:38)。 但是“ local_time”和“ offset_time”的值都奇怪(上午7:39)。

我想知道为什么会有这种行为,如果你们中有些人知道为什么我的两个时间字段不能正确存储值。

PS:版本:

  • 休眠:5.2.17.Final
  • 春季靴:2.0.4。发布

我的示例实体用于插入数据:

import javax.persistence.*;
import java.io.Serializable;
import java.time.*;
import java.util.Objects;

@Entity
@Table(name = "avdev_myData")
public class MyData implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "instant")
    private Instant instant;

    @Column(name = "local_date")
    private LocalDate localDate;

    @Column(name = "local_time")
    private LocalTime localTime;

    @Column(name = "offset_time")
    private OffsetTime offsetTime;

    @Column(name = "local_date_time")
    private LocalDateTime localDateTime;

    @Column(name = "offset_date_time")
    private OffsetDateTime offsetDateTime;

    @Column(name = "zoned_date_time")
    private ZonedDateTime zonedDateTime;

6 个答案:

答案 0 :(得分:1)

尝试一下:

@SpringBootApplication
public class YourApplication {

    @PostConstruct
    void started() {
        // set JVM timezone as UTC
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    }
}

答案 1 :(得分:1)

以防在我的情况下决定使用MySQL正常工作

  

spring.jpa.properties.hibernate.dialect =   org.hibernate.dialect.MySQL57InnoDBDialect

     

spring.datasource.url = jdbc:mysql:// DBHOST:3306 / DBNAME?useLegacyDatetimeCode = false&serverTimezone = UTC

答案 2 :(得分:1)

我在休眠错误跟踪器中打开了一个问题,并回答了我的问题。

对于LocalTime,转换是相对于1970年1月1日的,而不是我进行测试的日期。因此无法处理DST。

根据Vlad Mihalcea,我们必须改用LocalDateTime,因为我们知道日期,当然知道日期是否在夏令时。

这里有完整的答复: https://hibernate.atlassian.net/browse/HHH-12988?focusedCommentId=103750&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-103750

致谢

答案 3 :(得分:0)

spring.datasource.url=jdbc:mysql://...?serverTimezone=Asia/Shanghai

为我工作。

  1. 休眠应反映mysql的时区。
  2. mysql jdbc应该反映mysql的时区。

答案 4 :(得分:0)

一旦spring上下文被初始化...。

import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;


@Component
public class ApplicationStartUp {

    @EventListener(ContextRefreshedEvent.class)
    public void contextRefreshedEvent() {

        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

    }   

}

OR

@Component
public class InitializingContextBean implements InitializingBean {

    private static final Logger LOG  = Logger.getLogger(InitializingContextBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {         
            TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    }
}

答案 5 :(得分:0)

将默认时区设置为UTC不能完全解决我的问题。这就带来了另一个问题,当我记录带有OffsetTime属性的实体时,它以错误的方式吞下了偏移信息和数据保存的数据。例如,18:25 + 03:00变成18:25 + 00:00。我认为这是最糟糕的情况,因为数据已损坏。

为克服此问题且不丢失偏移量信息,我使用了OffsetTime类的withOffsetSameInstant方法并像这样记录了我的实体。

ZoneOffset systemZoneOffset = ZoneId.systemDefault().getRules().getOffset(Instant.now());
OffsetTime offsetTime = clientOffsetTime.withOffsetSameInstant(systemZoneOffset);

最后,这将适用于您计算机所使用的任何时区。这也适用于OffsetDateTime类型的属性。