Java - SQL时间戳的时区转换问题

标签 java mysql date calendar timezone

我在服务器上使用 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;
    }
}

最佳答案

tl;dr

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 或获取 UTC 中的当前时刻作为 Instant

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 中定义的合理实用格式。标准。

关于Java - SQL时间戳的时区转换问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44354478/

相关文章:

java - 使用 Java Spring Boot,如何将依赖项、我的源代码和 Spring 合并到一个 Jar 文件中

java - 如何自动更改 Jlabel

php - 行分页时如何检索列总数?

javascript - 在 JavaScript 中从整数构造新日期时检测无效日期值

javascript - 在 JavaScript 计数器中使用主机日期

java - @JsonView 在简单测试中不起作用

java - 每个抛出异常的语句的 try/catch 是否被视为反模式?

mysql - 按百分比排序汇总结果?

java - Hibernate 查询以选择 Datetime 列的时间范围内的行

mysql - 检索特定月份的数据