仍然被Java时间戳等与MySQL混淆

仍然被Java时间戳等与MySQL混淆,java,mysql,datetime,timestamp,Java,Mysql,Datetime,Timestamp,在搜索和阅读了如何处理Java和MySQL之间的日期和时间之后,我仍然感到困惑 假设我创建了一个java.util.Date对象。该对象以UTC表示时间。可以使用java.text.simpleDataFormat等工具对其他时区进行任何格式化或解析 现在我想将我的日期对象存储到UTC的MySQL数据库中。但是当我在java.sql.PreparedStatement中使用setTimestamp()方法时,我有点困惑。下面是一些示例代码,我在表中测试了MySQL日期时间和时间戳。我还使用set

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

假设我创建了一个
java.util.Date
对象。该对象以UTC表示时间。可以使用java.text.simpleDataFormat等工具对其他时区进行任何格式化或解析

现在我想将我的日期对象存储到UTC的MySQL数据库中。但是当我在
java.sql.PreparedStatement
中使用
setTimestamp()
方法时,我有点困惑。下面是一些示例代码,我在表中测试了MySQL日期时间和时间戳。我还使用
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)
第一列只是一个文本类型,我在其中存储UTC格式的日期

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

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

最后,对于第三行,我使用了
pst.setTimestamp(2,t,utc.getCalendar())
存储日期,希望驾驶员使用UTC时区。但显然不是(第2期)


通过将默认时区设置为UTC,我可以轻松解决以UTC存储日期的问题。不过,我还是想了解上面两个问题的进展情况。

MySql日期时间字段是非常棒的,只要你仔细想想

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

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


这一切中真正有趣的部分是,您可以在数据库连接上设置时区,并且您获取或插入的所有日期时间都将随着您的连接而改变

最后,我理解了一些东西,但改用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();
当使用带时区的时间戳时,我从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中,但都是正确的

如果我改为使用不带时区的
时间戳

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驱动程序似乎只是忽略了
setTimestamp()
中的
Calendar
参数。也许我遗漏了什么。

“从UTC返回当前时区进行检索”是您在输出中看到的内容。谢谢,但您可以对此进行扩展吗?时间戳在UTC内部存储,但当您使用
mysql
客户端输出时,它们会在本地时间显示给您,就像mysql文档告诉您的那样。第2期也一样。这里有什么问题?我的客户端和服务器mysql都在CET。我只在java代码中使用EST进行测试,这与我所说的无关。你的具体问题是什么?输出对我来说并不奇怪,它与您在文档中的引用一致。但我只使用SimpleDataFormat创建一个字符串,并将其输入到数据库的文本字段中。将时间戳直接插入DATETIME或Timestamp字段不使用SimpleDataFormat。