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
以提供纳秒级精度。 Date
和 Timestamp
都没有设计为将特定时区称为 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
类型的列保存LocalDateTime
和VARCHAR
保存时区,如"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 反序列化
Swagger:将 Java LocalDateTime 转换为 Joda LocalDateTime
解析 localDateTime 时无法从 Temporal Accessor 获取 LocalDateTime。我正在努力摆脱不到 30 天的几个月