在服务过程中,在我的情况下,在使用Java 8的Spring Boot API中,将LocaDateTime写入数据库的过程中,在数据库侧保存配置为TIMESTAMP的属性,在API中保存配置为LocaDateTime的属性时,该日期将保存为3当前操作系统日期之后的小时数。
假设我尝试完全在上午10点执行此操作,保存到数据库的日期应为11个小时,但就我而言,这种方式无法正常工作...
private LocalDateTime dataLimite;
@PrePersist
public void prepareToSave() {
String str = "1986-04-08 10:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);
this.dataLimite = dateTime.plusHours(1);
}
显然应该指出,这是Mysql配置方面的问题,但是当我测试时...
SELECT NOW();
结果恰好是操作系统的日期和时间,那么到底发生了什么真正的问题?如何解决这个问题?
最佳答案
LocalDateTime
仔细阅读文档。
此类不存储或表示时区。相反,它是对用于生日的日期的描述,以及在墙上时钟上看到的本地时间。如果没有其他信息(例如偏移量或时区),则无法在时间线上表示时刻。
Java中的LocalDateTime
类不能用于表示时刻。它代表了大约26-27小时(全球时区范围)内的潜在时刻。
此类包含日期和时间,但故意缺少时区或UTC偏移量的任何概念。因此,例如,如果您在今年1月23日中午存储,则不知道您是指在东京,加尔各答,巴黎或蒙特利尔中午……所有不同的时刻,相隔数小时,发生在东部较早的时间以及稍后发生的时间西方。
因此,您使用了错误的类。暂时使用Instant
,OffsetDateTime
或ZonedDateTime
。TIMESTAMP
请更仔细地阅读文档。
MySQL 8中的TIMESTAMP
类型类似于SQL标准类型TIMESTAMP WITH TIME ZONE
。此类型表示时刻,即时间轴上的特定点。
将提交给数据库的文档explains调整为UTC进行存储。
MySQL将TIMESTAMP
值从当前时区转换为UTC进行存储,然后从UTC返回当前时区进行检索。
这解释了您的问题。您隐式依赖于当前的默认时区来分配给Java中缺少任何时区的错误类型的对象。切勿编写依赖于服务器当前默认时区(或语言环境)的代码,因为这超出了程序员的控制范围。相反,请始终明确指定所需/期望的时区。更好的是,只要有可能就使用UTC。
为了获得最佳结果,在与数据库交换值时请坚持使用UTC。
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
恢复。
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
要查看某个特定地区的人们使用的挂钟时间的那一刻,请应用
ZoneId
以获得ZonedDateTime
。ZoneId z = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;