为啥 Java 和 DB 时区会自动更改?
Posted
技术标签:
【中文标题】为啥 Java 和 DB 时区会自动更改?【英文标题】:Why is the Java and DB time-zone automatically changed?为什么 Java 和 DB 时区会自动更改? 【发布时间】:2021-08-15 08:49:35 【问题描述】:我在使用java、spring、mysql时发现了一个未知问题。
mysql 时区 = UTC。
和 tomcat 时区 = KST。喜欢这段代码
@PostConstruct
void postConstruct()
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
现在,让我们检查一下 Java 时区。
TimeZone aDefault = TimeZone.getDefault();
System.out.println("aDefault = " + aDefault); // 'Asia/Seoul' It is what i wanted.
LocalDateTime = LocalDateTime.now();
System.out.println("localDateTime = " + localDateTime); // It is based on KST. what i wanted
问题是我在数据库上运行查询的那一刻。
//使用jpa,语义解释省略语法。
// jpa
@Query("select a from A a where datetime >= : dateTime")
明确确认查询是KST时间执行的,DB是UTC时间保存的,但是DB中导入的值符合基于KST的条件。
我真的不知道如何自动计算 DB 时间。
例如,应用程序将数据存储在 10 点钟位置,值存储在 1 点钟位置 (-9) 的 DB 中。
我立即运行查询,所以我检查了它是否发送到select ~ where datetime >= 10:00:00
。
那么,由于是1点钟存储在DB中,所以不导入数据是正常的,但是数据是正常导入的。
【问题讨论】:
datetime
列的类型是什么?是带时区还是不带时区?
【参考方案1】:
我不使用 Spring Boot 或 JPA。但我可以给你一些一般性的观点。
您的问题并不十分清楚,但您似乎正在尝试使用类似于 SQL 类型TIMESTAMP WITH TIME ZONE
的数据类型的列在表中查找行。对于 JDBC 4.2,您应该使用 OffsetDateTime
类,因为它是与 SQL 标准类型 TIMESTAMP WITH TIME ZONE
匹配的类型。
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
在直接的 JDBC 代码中,我将使用带有 ?
占位符和 SQL 的预处理语句,如下所示:
SELECT *
FROM some_table
WHERE some_column >= ?
;
在准备好的语句中,将OffsetDateTime
作为?
的值传递。
myPreparedStatement.setObject( … , odt ) ;
检索。
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
您可以将 OffsetDateTime
调整为特定时区。同一时刻,时间轴上的同一点,不同的挂钟时间。
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
关于您的示例代码......我想不出任何情况下调用LocalDateTime.now()
是正确的做法,因为该类型不能代表时刻,不是时间轴上的一个点。相反,使用Instant.now()
表示当前时刻(如UTC 所示),或ZonedDateTime.now()
表示当前时刻(如JVM 当前默认时区所示)。对于 JDBC 工作,请使用OffsetDateTime
,如上所示。
你说:
例如,应用程序将数据存储在 10 点钟位置,值存储在 1 点钟位置 (-9) 的 DB 中。
要么:
您在列上使用了不正确的数据类型。要记录时间线上的特定时刻,您必须使用TIMESTAMP WITH TIME ZONE
,而不是WITHOUT
。
在 MySQL 中,这意味着您需要使用 TIMESTAMP
,而不是 DATETIME
。
您正在使用自以为是的中间件或工具,这些工具具有在从数据库检索时注入时区调整的反特性。
永远不要使用TimeZone
类。该类是多年前被现代 java.time 类取代的遗留日期时间类的一部分。具体替换为ZoneId
& ZoneOffset
。
最后,我几乎没有提到这个警告:如果您要预订未来的约会或类似的事情,这是错误的方法。如果您希望即使政客改变您的时区规则也能保持固定时间的预约(因此,无论夏令时或其他时钟操作是否发生变化,下午 3 点的牙科预约都将保持在下午 3 点),那么您应该使用 两个 列,一个类似于 SQL 标准类型 TIMESTAMP WITHOUT TIME ZONE
。并使用包含预期时区名称的文本类型的第二列。当需要在需要特定时刻的地方动态创建日历时,请在运行时组合这两个值以生成 ZonedDateTime
对象。那一刻只能在运行时短暂使用,从不存储。这个话题已经在 Stack Overflow 上讨论过很多次了。搜索以了解更多信息。
【讨论】:
以上是关于为啥 Java 和 DB 时区会自动更改?的主要内容,如果未能解决你的问题,请参考以下文章
为啥更改 UIImageView 内容模式会影响其在 Xcode6 中的自动布局?
为啥我在AppGallery Connect中为其他应用更改货币时,我的应用中的应用内产品价格的货币会自动更改?