从MySQL DB选择

时间:2019-02-13 09:21:56

标签: java mysql date jdbc timezone

我遇到了与时区有关的问题。

我在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 /服务器的时区。

2 个答案:

答案 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(始终停留在“东部标准时间”)。