【问题标题】:Java - TimeZone conversion issue with SQL timestampJava - SQL 时间戳的时区转换问题
【发布时间】:2017-11-05 08:57:26
【问题描述】:

我在服务器上使用 MySQL 数据库。我的 Web 应用程序服务器位于 US/Chicago,但我希望将 DateTime 值保存在 Africa/Lagos 时间。

我从 Stack Overflow 获得了这段代码的一部分,但是当我更新一条记录时,它仍然在数据库记录中显示芝加哥时间。 请问我在这里做错了什么?

public static boolean saveImageFileName(int id, String fileName) throws SQLException, IOException, IllegalArgumentException, ClassNotFoundException
{
    try(Connection conn = Config.getDatabaseConnection())
    {
        //Create a timezone object based on Africa/Lagos time zone
        SimpleTimeZone timeZone = new SimpleTimeZone(1, "Africa/Lagos");
        //Create a calendar object using the timezone object
        GregorianCalendar calendar = new GregorianCalendar(timeZone);
        //Create a timestamp object using the calendar's date and time
        Timestamp timestamp = new Timestamp(calendar.getTime().getTime());
        //Create an SQL query String
        String sql = "UPDATE `requests` SET `filename` = ?, `uploaded_date` = ? WHERE `id` = ?";
        //Create a prepared statement object from database connection
        PreparedStatement pst = conn.prepareStatement(sql);
        //Set prepared statement parameters
        pst.setString(1, fileName);
        pst.setTimestamp(2, timestamp, calendar); //Set TimeStamp and Calendar
        pst.setInt(3, id);
        //Update the record
        int update = pst.executeUpdate();
        //return update result
        return update == 1;
    }
}

【问题讨论】:

    标签: java mysql date calendar timezone


    【解决方案1】:

    tl;博士

    pst.setObject( 2 , Instant.now() ) ;  // Current moment in UTC.
    

    ……和……

    Instant instant = myResultSet.getObject( 2 , Instant.class ) ;  // Stored moment in UTC.
    ZonedDateTime zdt = instant.atZone( ZoneId.of( "Africa/Lagos" ) ) ;   // Adjust from UTC to time zone. Same moment, same point on timeline, but different wall-clock time.
    

    不要依赖toString

    显然您对遗留日期时间类的toString 方法的输出感到困惑。在这些旧类中的许多糟糕的设计决策中,有一个反特性是应用 JVM 的当前时区,同时生成一个字符串来以文本方式表示该日期时间对象的值。这会产生令人困惑的错觉,即对象具有时区,而实际上却没有。

    所以你不能在那些旧的类上使用toString,因为它不能忠实地代表真正的价值。

    使用 java.time

    您正在使用麻烦的旧日期时间类,这些类现在是遗留的,被 java.time 类所取代。

    ZoneId z = ZoneId.of( "Africa/Lagos" ) ) ;
    ZonedDateTime zdt = ZonedDateTime.now( zdt ) ;
    

    但时区在这里无关紧要,因为 java.sql.Timestamp 始终采用 UTC。等价于 java.time 中的 Instant。您可以从ZonedDateTime 中提取Instant 或以Instant 的形式获取UTC 中的当前时刻。

    Instant instant = zdt.toInstant() ;  // Adjusting from a time zone to UTC.
    

    或者……

    Instant instant = Instant.now() ;  // Current moment in UTC. 
    

    如果您的 JDBC 驱动程序符合 JDBC 4.2 或更高版本,您可以直接使用 java.time 类型并放弃传统的 java.sql 类型。致电PreparedStatement::setObjectResultSet::getObject

    如果您的 JDBC 尚未针对 java.time 进行更新,请使用添加到旧类的新方法转换为 java.sql.Timestamp

    java.sql.Timestamp ts = java.sql.Timestamp.from( instant ) ;
    

    另一方面,当您想通过特定地区的挂钟时间的镜头看到同一时刻时,您可以将 Instant 调整到特定时区。

    Instant instant = myResultSet.getObject( … , Instant.class )  // Or call: ts.toInstant()
    ZonedDateTime zdt = instant.atZone( z ) ;
    

    至于生成 String 对象以文本形式表示这些 java.time 对象的值,您可以依赖于调用toString 方法。 java.time 类默认使用ISO 8601 标准中定义的合理实用格式。

    【讨论】:

      【解决方案2】:

      您需要在setTimeZone 方法中使用TimeZone 传递Calendar 对象,如this javadoc 中所述,试试这个:

      pst.setTimestamp(2, timestamp, Calendar.getInstance(TimeZone.getTimeZone("Africa/Lagos"))); 
      

      【讨论】:

      • 它仍然显示芝加哥时间 2017-06-04 08:31:35 而不是 2017-06-04 14:31:35
      猜你喜欢
      • 2018-04-17
      • 1970-01-01
      • 2011-05-21
      • 2020-06-15
      • 1970-01-01
      • 2011-12-15
      • 1970-01-01
      • 1970-01-01
      • 2020-07-01
      相关资源
      最近更新 更多