仍然被Java Timestamps等与MySQL混淆

时间:2012-03-22 14:55:45

标签: java mysql datetime timestamp

在搜索和阅读如何处理来自Java和MySQL的日期和时间后,我仍然感到困惑。

假设我创建了一个java.util.Date对象。该对象保留UTC时间。任何格式化或解析到其他时区都可以用例如java.text.SimpleDateFormat

现在我想将我的日期对象以UTC格式存储到MySQL数据库中。但是当我在setTimestamp()中使用java.sql.PreparedStatement方法时,我有点困惑。下面是一些示例代码,我在表中测试了MySQL DATETIME和TIMESTAMP。我还使用setString()setTimestamp()方法插入日期。

java.sql.Connection conn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost/test","user","password");
java.sql.Statement st = conn.createStatement();

String q = "DROP TABLE IF EXISTS tmp";
st.execute(q);
q = "CREATE TABLE tmp (dt_string TEXT, dt DATETIME, ts TIMESTAMP)";
st.execute(q);

java.sql.PreparedStatement pst = conn.prepareStatement("INSERT INTO tmp SET dt_string=?, dt=?, ts=?");

java.text.SimpleDateFormat utc = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
utc.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("EST"));

System.out.println("Default time zone: " + java.util.TimeZone.getDefault().getID());
java.util.Date d = new java.util.Date();
System.out.println("A date: " + d);
java.sql.Timestamp t = new java.sql.Timestamp( d.getTime() );
System.out.println("The timestamp: " + t);


pst.setString(1, utc.format(d) );
pst.setString(2, utc.format(d) );
pst.setString(3, utc.format(t) );
pst.execute();

pst.setTimestamp(2, t);
pst.setTimestamp(3, t);
pst.execute();

System.out.println("Use calendar: " + utc.getCalendar().getTimeZone() );
pst.setTimestamp(2, t, utc.getCalendar());
pst.setTimestamp(3, t, utc.getCalendar());
pst.execute();

conn.close();

当我运行上面的内容时,我得到以下输出,这是预期的。

Default time zone: EST
A date: Thu Mar 22 08:49:51 EST 2012
The timestamp: 2012-03-22 08:49:51.784
Use calendar: sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]

但是当我使用MySQL命令行工具检查数据库中的表时,我得到:

mysql> select * from tmp;
+---------------------+---------------------+---------------------+
| dt_string           | dt                  | ts                  |
+---------------------+---------------------+---------------------+
| 2012-03-22 13:49:51 | 2012-03-22 13:49:51 | 2012-03-22 13:49:51 |
| 2012-03-22 13:49:51 | 2012-03-22 08:49:51 | 2012-03-22 08:49:51 |
| 2012-03-22 13:49:51 | 2012-03-22 08:49:51 | 2012-03-22 08:49:51 |
+---------------------+---------------------+---------------------+
3 rows in set (0.00 sec)

第一列只是一个TEXT类型,我存储UTC格式的日期。

在第一行中,我使用setString()方法存储了日期。

在第二行中,我使用setTimestamp(i,t)方法存储了日期。我想在存储它之前,JDBC会使用默认时区(我设置为EST)自动转换日期。但是TIMESTAMP不应该总是自动存储在UTC中。 MySQL文档说 MySQL将TIMESTAMP值从当前时区转换为UTC进行存储,然后从UTC返回到当前时区进行检索。(问题1)。

最后,对于第三行,我使用pst.setTimestamp(2, t, utc.getCalendar());来存储日期,希望驱动程序应该使用UTC时区。但显然不是(问题2)。

我可以通过将默认时区设置为UTC来轻松解决以UTC格式存储日期的问题。但是,我仍然想了解上述两个问题的进展情况。

2 个答案:

答案 0 :(得分:1)

最后,我理解了一些东西,但改用了PostgreSQL。我基本上使用

重复上面的代码
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost");

st.execute("CREATE TABLE tmp (ts_string TEXT, ts TIMESTAMP WITH TIME ZONE)");
//st.execute("CREATE TABLE tmp (ts_string TEXT, ts TIMESTAMP WITHOUT TIME ZONE)");

并将日期写为

pst.setString(1, utc.format(d));
pst.setTimestamp(2, t);
pst.execute();

pst.setTimestamp(2, t, utc.getCalendar());
pst.execute();

使用TIMESTAMP WITH TIMEZONE时,我会从psql

获得以下输出
postgres=# select * from tmp;
        ts_string        |             ts             
-------------------------+----------------------------
 2012-03-23 12:48:28.057 | 2012-03-23 13:48:28.057+01
 2012-03-23 12:48:28.057 | 2012-03-23 13:48:28.057+01
(2 rows)

因为它跟踪系统(服务器)时区,即CET(+01)。虽然在CET中显示,但日期是正确的。

如果我改为使用TIMESTAMP WITHOUT TIME ZONE

postgres=# select * from tmp;
        ts_string        |           ts           
-------------------------+------------------------
 2012-03-23 12:49:04.120 | 2012-03-23 07:49:04.12
 2012-03-23 12:49:04.120 | 2012-03-23 12:49:04.12
(2 rows)

并且在第一种情况下它使用默认时区(我设置为EST),日期自然是“错误的”,就像MySQL的情况一样。但是,在第二种情况下,它使用UTC,因为我将其作为setTimestamp()方法的参数传递,这就是我在MySQL中尝试做的事情。对我来说,似乎MySQL java驱动程序只是忽略了Calendar中的setTimestamp()参数。也许我错过了什么。

答案 1 :(得分:0)

MySql datetime字段在您绕过它们时非常棒。

您正在使用的“SimpleDateFormat”没有附加时区,因此您的数据库假定您在服务器的时区中输入日期。如果您在-0500时区,它会增加5个小时将其转换为UTC,然后当您获取它时,它会减去5个小时,然后将其转换回当地时间。

在插入之前将时间戳转换为utc时,仍然没有在字符串上添加时区。您的数据库对其执行了相同的操作。 2012-03-22 13:49:51增加了5个小时,所以你的数据库存储2012-03-22 18:49:51(仍然假设-0500)。

所有这一切真正有趣的部分是你可以在数据库连接上设置时区,你获取或插入的所有日期时间都会随着你的连接而变化!