在 JPA MySql 中存储与时区无关的时间的最佳方法

Posted

技术标签:

【中文标题】在 JPA MySql 中存储与时区无关的时间的最佳方法【英文标题】:Best way to store timezone-independent time in JPA MySql 【发布时间】:2020-05-30 17:33:06 【问题描述】:

我在 JPA 中找不到一个很好的解决方案来在 mysql 数据库中存储与时区无关的时间。

我有一个班次表,我需要在其中存储每个班次的开始和结束时间。

+-------------+--------------+------+-----+----------+----------------+
| Field       | Type         | Null | Key | Default  | Extra          |
+-------------+--------------+------+-----+----------+----------------+
| id          | int(11)      | NO   | PRI | NULL     | auto_increment |
| name        | varchar(64)  | NO   |     | NULL     |                |
| start_time  | time         | NO   |     | 00:00:00 |                |
| end_time    | time         | NO   |     | 00:00:00 |                |
+-------------+--------------+------+-----+----------+----------------+

因此,从 mysql 检索的开始/结束时间必须被视为字符串(不应用特定时区)。但是,如果我将它们存储为 varchar(8),我将无法使用以下查询(不应用从 varchar 到时间的任何类型转换)。

select * from shift where ('21:30:00' BETWEEN start_time and end_time );

所以,我想保留将它们作为时间进行比较的能力。

我尝试将班次表建模如下:

    @Entity
    public class Shift 
      @Id
      private Long id;

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

      @Column(name = "start_time")
      private LocalTime startTime;

      @Column(name = "end_time")
      private LocalTime endTime;

...

start_time 和 end_time 映射为 LocalTime,因为 LocalTime 不存储时区信息。但是,Hibernate 将检索到的每条记录的 java.util.Date 转换为应用系统默认时区的 LocalTime,如下所示:

    if ( Date.class.isInstance( value ) ) 
        final Date ts = (Date) value;
        final Instant instant = Instant.ofEpochMilli( ts.getTime() );
        return LocalDateTime.ofInstant( instant, ZoneId.systemDefault() ).toLocalTime();
    

因此,生成的开始/结束时间与服务器的时区有关。

解决方案是将它们存储为整数并将小时与分钟分开。这样,班次表就会有四列:start_hours、start_minutes、end_hours、end_minutes。但是在 JPA 中使用它们进行查询很乏味。 有更好的解决方案吗?

更新

我必须使用服务器的时区获取当前班次。因此,如果我将时间作为 UTC 存储在数据库中,然后我查询它们,将当前服务器时间转换为 UTC 不起作用,因为在夏令时 (DST) 期间,服务器时区增加一。因此,在冬季,07:00:00+01 转换为 06:00:00 UTC。但是,在夏季 07:00:00+02 转换为 05:00:00 UTC。

【问题讨论】:

【参考方案1】:

欢迎来到 SO

最好将带有 UTC TimeZone 的日期存储在数据库中并在客户端进行转换。您可以设置以下属性以强制 JPA 使用 UTC

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

希望能帮到你

你可以看看this blog

【讨论】:

谢谢。如果我将 time_zone 设置为 UTC,则休眠会将查询中用作参数的时间转换为 UTC。但是,当我从班次表中读取数据时,休眠会应用服务器的时区。因此,最简单的解决方案是更改服务器的时区,但有时不可能,因为基于服务器上设置的时区,还有其他经过测试的功能。 你是如何安置房产的? 如您在答案中所述。我必须使用服务器的时区来获取当前班次。因此,如果我将时间作为 UTC 存储在数据库中,然后我查询它们,将当前服务器时间转换为 UTC 不起作用,因为在夏令时 (DST) 期间,服务器时区增加一。因此,在冬季,07:00:00+01 转换为 06:00:00 UTC。但是,在夏季 07:00:00+02 转换为 05:00:00 UTC。

以上是关于在 JPA MySql 中存储与时区无关的时间的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JPA 和 Hibernate 在 UTC 时区中存储日期/时间和时间戳

如何使日期与语言环境无关?

JPA 和服务器时区错误

Spring jpa hibernate mysql LocalDate在坚持一天后关闭

获取指定时区的日期时间,与计算机中设置的日期时间无关

如何使用时区信息在 MySQL 中存储日期时间