简体   繁体   English

UTC 时区问题上的休眠 + Spring Boot 存储日期

[英]hibernate + spring boot store date on UTC time zone troubles

I'm making some tests to define UTC as default time zone for my application.我正在做一些测试以将 UTC 定义为我的应用程序的默认时区。 First of all I want my datetime values to be stored with the UTC one.首先,我希望我的日期时间值与 UTC 值一起存储。

According to VLAD MIHALCEA ( https://vladmihalcea.com/how-to-store-date-time-and-timestamps-in-utc-time-zone-with-jdbc-and-hibernate/ ) and https://moelholm.com/2016/11/09/spring-boot-controlling-timezones-with-hibernate/ I have set in my properties file:根据 VLAD MIHALCEA ( https://vladmihalcea.com/how-to-store-date-time-and-timestamps-in-utc-time-zone-with-jdbc-and-hibernate/ ) 和https://moelholm .com/2016/11/09/spring-boot-controlling-timezones-with-hibernate/我在我的属性文件中设置:

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

For testing I'm using h2 database, I've created a sample entity with all java 8 dateTime Type.为了测试我使用的是 h2 数据库,我创建了一个包含所有 java 8 dateTime 类型的示例实体。

In my liquibase config they are defined like this:在我的 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"/>

I think I'm using the good type for every fields.我想我在每个领域都使用了良好的类型。 It's work for all fields except for "local_time" "offset_time" which are Time sql types not timestamp.它适用于除“local_time”“offset_time”之外的所有字段,它们是时间 sql 类型而不是时间戳。

在此处输入图片说明

As you can see i added this row at 8:39am (Paris GMT+2) and timestamps have the good UTC value (6:38am).如您所见,我在上午 8:39(巴黎 GMT+2)添加了这一行,时间戳具有良好的 UTC 值(上午 6:38)。 BUT both "local_time" and "offset_time" have a strange value (7:39am).但是“local_time”和“offset_time”都有一个奇怪的值(7:39am)。

I wonder why this behaviour, if some of you have an idea why my two time fields not store values correctly.我想知道为什么这种行为,如果你们中的一些人知道为什么我的两个时间字段不能正确存储值。

PS: version: PS:版本:

  • Hibernate: 5.2.17.Final休眠:5.2.17.Final
  • Spring boot: 2.0.4.RELEASE弹簧靴:2.0.4.RELEASE

My sample Entity use to insert data :我的示例实体用于插入数据:

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;

Have a try:试试:

@SpringBootApplication
public class YourApplication {

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

In case decide using MySQL in my case works properly with如果决定在我的情况下使用 MySQL 可以正常工作

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

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

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

works for me.为我工作。

  1. Hibernate SHOULD reflect mysql timezone. Hibernate 应该反映 mysql 时区。
  2. mysql jdbc should reflect mysql timezone. mysql jdbc 应该反映 mysql 时区。

I opened an issue in the hibernate bug tracker and had an answer of my problem.我在休眠错误跟踪器中打开了一个问题,并得到了我的问题的答案。

For LocalTime the tranformation is relative to the 1st january 1970 not the day i had run the test.对于 LocalTime,转换是相对于 1970 年 1 月 1 日而不是我运行测试的那一天。 So the DST is not handled.所以不处理 DST。

According to Vlad Mihalcea we have to use LocalDateTime instead because we know the date and of course if it's on DST period or not.根据 Vlad Mihalcea 的说法,我们必须改用 LocalDateTime,因为我们知道日期,当然也知道它是否在 DST 期间。

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

regards问候

Once the spring context is initialized ....一旦 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"));
    }
}

Setting the default timezone as UTC didn't resolve my problem completely.将默认时区设置为 UTC 并没有完全解决我的问题。 It brought another problem, when I recorded an entity with OffsetTime property, it swallowed the offset info and data saved data in wrong way.它带来了另一个问题,当我记录一个带有 OffsetTime 属性的实体时,它以错误的方式吞下了偏移信息和数据保存的数据。 For example, 18:25+03:00 turns into 18:25+00:00.例如,18:25+03:00 变为 18:25+00:00。 I think this is the worst scenario, because data becomes corrupted.我认为这是最糟糕的情况,因为数据已损坏。

To overcome this issue and not lose offset info, I used withOffsetSameInstant method of the OffsetTime class and record my entity like that.为了克服这个问题并且不丢失偏移信息,我使用了 OffsetTime 类的 withOffsetSameInstant 方法并像这样记录我的实体。

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

Lastly, this will work for any timezone that your computer is using.最后,这适用于您的计算机正在使用的任何时区。 This should also work for OffsetDateTime type of properties.这也适用于 OffsetDateTime 类型的属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM