LocalDateTime , ZonedDateTime 和时间戳

Posted

技术标签:

【中文标题】LocalDateTime , ZonedDateTime 和时间戳【英文标题】:LocalDateTime , ZonedDateTime and Timestamp 【发布时间】:2017-10-15 13:45:38 【问题描述】:

我有一个 SpringBoot 应用程序。使用 Spring Initializer、嵌入式 Tomcat、Thymeleaf 模板引擎,并打包为可执行 JAR 文件。

我有一个具有 2 个属性(initDate、endDate)的域对象。我想创建 2 个转换器来处理 mysql DB

@Convert(converter = LocalDateTimeAttributeConverter.class) 
private LocalDateTime initDate;

@Convert(converter = ZonedDateTimeAttributeConverter.class) 
private ZonedDateTime endDate;

转换器1(没问题)

@Converter
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> 

    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime localDateTime) 
        return (localDateTime == null ? null : Timestamp.valueOf(localDateTime));
    

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) 
        return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
    

这是我要创建的一个

@Converter
public class ZonedDateTimeAttributeConverter implements AttributeConverter<ZonedDateTime, Timestamp> 

    @Override
    public Timestamp convertToDatabaseColumn(ZonedDateTime zoneDateTime) 
        return (zoneDateTime == null ? null : Timestamp.valueOf(zoneDateTime));
    


    @Override
    public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) 
        return (sqlTimestamp == null ? null : sqlTimestamp.toZonedDateTime());
    

但我不能,因为我有 2 个错误:

The method valueOf(String) in the type Timestamp is not applicable for the arguments (ZonedDateTime)

而TimeStamp没有toZonedDateTime()的方法

如果我没有为 ZonedDate 添加任何转换器,JPA 会创建一个类型为 varbinary(255) 的表

【问题讨论】:

那么您希望它在哪个时区? (看起来很糟糕,无法将 Timestamp 转换为 LocalDateTime,但我们开始了。)你可能想通过 Timestamp.toInstant()Timestamp.from(Instant) 去。 【参考方案1】:

Timestamp 扩展 Date 以提供纳秒级精度。 DateTimestamp 都没有设计为将特定时区称为 ZoneDateTime

如果您需要转换 ZonedDateTime -> Timestamp 您将不得不丢弃时区/偏移信息。例如

LocalDateTime withoutTimezone = zoneDateTime.toLocalDateTime();
Timestamp timestamp = Timestamp.valueOf(withoutTimezone));

对于转换 Timestamp -> ZonedDateTime 你需要指定一个偏移量:

LocalDateTime withoutTimezone = sqlTimestamp.toLocalDateTime();
ZonedDateTime withTimezone = withoutTimezone.atZone(ZoneId.of("+03:00"));

或时区:

ZonedDateTime withTimezone = withoutTimezone.atZone(ZoneId.of("Europe/Paris"));

如果您打算将ZonedDateTime 变量保存在数据库中并保留在那里指定的各种时区,我建议您相应地设计您的数据库。建议:

    使用DATETIME 类型的列保存LocalDateTimeVARCHAR 保存时区,如"Europe/Paris"SMALLINT,以分钟为单位保存偏移量。 将ZonedDateTime 转换为String 并保存在VARCHAR 列中,如"2017-05-16T14:12:48.983682+01:00[Europe/London]"。然后,您必须在从数据库中读取数据时对其进行解析。

【讨论】:

赞成设计数据库以保存时区信息的明显好建议。 withoutTimezone.atZone(ZoneId.of("Europe/Paris")) 与 sqlTimestamp.toInstant().atZone(ZoneId.of("Europe/Paris")) 相同,只是简单地添加了时区。【参考方案2】:

乔恩·斯基特已经说过了:

@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime zoneDateTime) 
    return zoneDateTime == null ? null : Timestamp.from(zoneDateTime.toInstant());


@Override
public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) 
    return sqlTimestamp == null ? null : sqlTimestamp.toInstant().atZone(ZoneId.systemDefault());

Jon 还问了一个很好的问题,你想要哪个时区?我猜到了ZoneId.systemDefault()。显然,不同的时区会产生不同的结果,所以我希望您三思而后行,并能够找到适合您的目的的正确时区。

PS 我减少了括号的使用,因为我发现用更少的括号更易读。如果您愿意,可以重新添加它们。

【讨论】:

以上是关于LocalDateTime , ZonedDateTime 和时间戳的主要内容,如果未能解决你的问题,请参考以下文章

LocalDateTime - 使用 LocalDateTime.parse 反序列化

java 根据类中LocalDateTime排序类集合

Swagger:将 Java LocalDateTime 转换为 Joda LocalDateTime

Java LocalDateTime实用方法

解析 localDateTime 时无法从 Temporal Accessor 获取 LocalDateTime。我正在努力摆脱不到 30 天的几个月

Java LocalDateTime 计算时间差