Java - 带有SQL时间戳的TimeZone转换问题

时间:2017-06-04 13:14:19

标签: java mysql date calendar timezone

我在服务器上使用MySQL数据库。我的Web应用程序服务器位于美国/芝加哥,但我希望我的DateTime值保存在非洲/拉各斯时间。

我从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;
    }
}

2 个答案:

答案 0 :(得分:2)

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。您可以从Instant中提取ZonedDateTime或以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标准中定义的合理实用格式。

答案 1 :(得分:0)

您需要在Calendar方法中使用TimeZone传递setTimeZone对象,如this javadoc中所述,请尝试以下操作:

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