我遇到了与时区有关的问题。
我在JAVA和MySql数据库中有一个程序。 我不使用任何ORM,但是我有使用mysql连接器Java v 6.0.4的自定义jdbc库
我将设置作为连接字符串的一部分发送为:
serverTimezone - America/Chicago
useTimezone - true
useJDBCCompliantTimezoneShift - false
useLegacyDatetimeCode - false
我尝试从数据库中选择一个日期,例如2019-02-13
。
数据库位于美国/东部时区,运行Java程序的服务器位于美国/芝加哥时区。
无法更改任何服务器的位置/时区。
在Java中,我将日期倒退了一天(2019-02-12
)。
问题是由于时间戳引起的-
1550034000
在美国/东部是2019-02-13 00:00:00
但是
1550034000
在美国/芝加哥是2019-02-12 23:00:00
因此,我得到了日期为java.sql.Date
的{{1}}对象。
由于时间信息已从日期开始中断,因此添加时区偏移没有帮助。
您能否提出一些解决方案,以解决如何在不更改时区的情况下获得正确的日期?
编辑:我正在使用2019-02-12
设置,但是我不确定该值应该是数据库正在使用的时区,还是只是覆盖运行该应用程序的JVM /服务器的时区。
答案 0 :(得分:3)
首先,我了解到您无法使用此功能,但对于其他读者,我想指出,一般的建议是至少在跨多个时区的情况下,以UTC运行所有内容。因此,这将是解决您问题的最佳方法。
第二,正如我和Gord Thompson在评论中都提到的那样,第二好的解决方案是将日期处理为LocalDate
,而不是java.sql.Date
。尽管后者只是假装没有时间,但是它确实存在设计问题,很难解决您的问题。 LocalDate
实际上是 日期,没有一天的时间,也没有时区,因此,下注应该更加安全(除非数据库驱动程序在{{1 }};我的手指不停;再次使用UTC运行所有内容也将消除这些错误)。 编辑:假设您可以修改自定义JDBC库,那么如何从LocalDate
获取LocalDate
:
ResultSet
它至少需要JDBC 4.2,您可能有。
如果以上都不可用,则可以使用这种方法来修复从数据库中获取的错误 LocalDate correctDateDirectlyFromDatabase
= yourResultSet.getObject("yourDateColumn", LocalDate.class);
。有点hack,但是可以用。
Date
当我在美国/芝加哥时区运行此命令时,它会打印:
import java.sql.Date; // … // Modern ID of the time zone previously known as US/Eastern ZoneId datebaseTimeZone = ZoneId.of("America/New_York"); Date dateFromDatabase = new Date(TimeUnit.SECONDS.toMillis(1_550_034_000)); System.out.println("Date as retrieved from database (or pretending): " + dateFromDatabase); long epochMillis = dateFromDatabase.getTime(); ZonedDateTime dateTime = Instant.ofEpochMilli(epochMillis) .atZone(datebaseTimeZone); LocalDate realDate = dateTime.toLocalDate(); // Sanity check if (! realDate.atStartOfDay(datebaseTimeZone).equals(dateTime)) { throw new IllegalStateException("Failed to convert date correctly from " + datebaseTimeZone + " time zone"); } System.out.println("Date is " + realDate);
我尝试在其他时区运行它。在某些时区,第一行打印Date as retrieved from database (or pretending): 2019-02-12
Date is 2019-02-13
,在其他时区,2019-02-12
。最后一行在我尝试过的所有时区中显示2019-02-13
。
现在,我给了您2019-02-13
。很好,这是您在后续处理中应该使用的。如果您需要LocalDate
用于尚未更改的其他旧版API,请通过以下方式转换回正确 java.sql.Date
:
java.sql.Date
日期转换回2019-02-13
当我说正确时,它要求没有人会篡改JVM的默认时区,这对于JVM中运行的任何程序都很容易做到。
链接: Oracle tutorial: Date Time解释了如何使用java.time。
答案 1 :(得分:2)
我正在使用
serverTimezone
设置,但是我不确定值应该是数据库正在使用的时区,还是只是覆盖运行应用程序的JVM /服务器的时区。
serverTimezone=America/Chicago
的意思是“不管服务器配置为使用默认时区如何,都将服务器的结果解释为好像服务器正在使用America/Chicago
时区一样”。因此,即使您在连接字符串中使用了该设置,即使服务器的默认时区显然是Timestamp
,也会将America/Chicago
的值转换为America/New_York
。
但是,在采用该方法之前,您需要确认服务器确实在使用America/New_York
(大概会在东部标准时间和东部夏令时间之间来回切换),而不是像UTC-5(始终停留在“东部标准时间”)。