LocalDateTime 、 ZonedDateTime 和时间戳
LocalDateTime , ZonedDateTime and Timestamp
我有一个 Spring启动应用程序。使用 Spring 初始化程序,嵌入 Tomcat,Thymeleaf 模板引擎,并打包为 executable 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)
并且时间戳没有方法toZonedDateTime()
如果我不为 ZonedDate 添加任何转换器,JPA 会创建一个类型为 varbinary(255)
的 table
Jon Skeet 已经说过了:
@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 我减少了括号的使用,因为我发现括号更少,可读性更强。如果您愿意,可以将它们添加回去。
Timestamp 扩展 Date
以提供纳秒精度。 Date
和 Timestamp
都不是用来指代特定时区的 ZoneDateTime
。
如果您需要转换 ZonedDateTime
-> Timestamp
,您将不得不丢弃 timezone/offset 信息。例如
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]"
。然后你必须在从数据库中读取时解析它。
我有一个 Spring启动应用程序。使用 Spring 初始化程序,嵌入 Tomcat,Thymeleaf 模板引擎,并打包为 executable 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)
并且时间戳没有方法toZonedDateTime()
如果我不为 ZonedDate 添加任何转换器,JPA 会创建一个类型为 varbinary(255)
Jon Skeet 已经说过了:
@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 我减少了括号的使用,因为我发现括号更少,可读性更强。如果您愿意,可以将它们添加回去。
Timestamp 扩展 Date
以提供纳秒精度。 Date
和 Timestamp
都不是用来指代特定时区的 ZoneDateTime
。
如果您需要转换 ZonedDateTime
-> Timestamp
,您将不得不丢弃 timezone/offset 信息。例如
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]"
。然后你必须在从数据库中读取时解析它。