MySQL – 如何用正确的时区存储时间? (来自Java)

我正在使用MySQL 5.0。 我需要将日期时间信息存储在一列中。 我的意思是使用DATETIMETIMESTAMP列类型。 但我有夏季 – 冬季时间变化的问题。

我的时区是CET。 夏天我们使用夏令时(CEST,它是GMT + 2),冬天我们使用冬季时间(CET,它是GMT + 1)。 在夏季时间变为冬季的那一天(2012年是在10月28日)我们有时间凌晨2点两次。 这一天的时间顺序是:

 ... -> 1:00 CEST -> 2:00 CEST -> 2:00 CET (= 3:00 CEST) -> 3:00 CET -> ... 

所以,如果我有时间戳/日期时间’2012-10-28 02:00:00’如果此时间代表夏令时或冬季时间凌晨2点,我无法正确说出。

我有两个不同的Java日期:

 Date d1 = new Date(1351382400000); // 2:00 CEST (summer-time) Date d2 = new Date(1351386000000); // 2:00 CET (winter-time) 

当我使用标准时间戳/日期时间格式(即'yyyy-MM-dd HH:mm:ss' )将它们存储在数据库中时,它们都存储相同的数据’2012-10-28 02:00:00′ 。 但是当我从数据库中将这些值返回到Date变量时,我得到两个相同的日期。 因此输入日期不同,但输出日期相同。
如果我使用FROM_UNIXTIME函数来存储日期值,则会出现相同的情况: FROM_UNIXTIME(1351382400)FROM_UNIXTIME(1351386000) 。 值作为相等的值存储在数据库列(DATETIME或TIMESTAMP类型)中。 因此,当我将这些值输入Java的Date对象时,我再次获得两个相同的日期(在冬季)。

有没有办法在MySQL中存储时区,或者如何处理DATETIME / TIMESTAMP列中的时区信息?
当然,我可以使用unix-timestamp将BIGINT值存储在数据库中。 但我想知道是否有任何方法可以解决任何MySQL日期时间类型的问题。

感谢任何帮助或技巧…… 🙂

非常感谢。

Honza(sporak)

编辑#1:
如果我试图存储时间戳值,然后将此时间戳值提取到java的日期,我再次得到错误的日期。 假设我有一个带有TIMESTAMP列的简单表。 我以这种方式将数据存储在此表中:

 mysql> INSERT INTO `tab` (timestamp_column) VALUES (FROM_UNIXTIME(1351382400)), // 2:00 CEST (summer-time) (FROM_UNIXTIME(1351386000)); // 2:00 CET (winter-time) Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0 

当我将这些行提取到Java Date对象中时,我得到两个显示2:00 CET的相同日期。 更重要的是 – 当我在MySQL中获取这些行时,我在MySQL中再次得到错误的值:

 mysql> SELECT UNIX_TIMESTAMP(timestamp_column) from tab; +--------------------+ | UNIX_TIMESTAMP(ts) | +--------------------+ | 1351386000 | | 1351386000 | +--------------------+ 2 rows in set (0.00 sec) 

所以TIMESTAMP对我来说似乎没什么用处。

在我看来,你最好的选择是告诉MySQL使用GMT并处理应用程序代码中的所有本地时间问题,而不是数据库。 数据库中的值始终是GMT,句号,这是明确的。 正如您所说,通过夏令时(夏令时)调整,您可以在数据库中获得相同的值,对于我们人类来说,两次不同的时间。

这也使数据库可移植。 如果你移动到北美并开始使用设置为(比如)中央时间的MySQL,那么数据库中的值似乎已经移动了几个小时。 我有一个我inheritance的数据库的问题,这个数据库是使用服务器的本地时间,当我把它从美国东海岸移到西海岸时,没想过要检查MySQL是否从属于机器的区域…

DATETIMEYEARDATETIME都存储相应的日期/时间指示 ,就像拍摄日历和/或表盘的照片一样:它们不记录时钟设置的时区,因此它们不代表任何特定的时间这一刻。 它们对于在特定的当地日期或特定的当地时间 (即与时区无关)发生的事件非常有用。

TIMESTAMP存储UTC时间戳(自UNIX纪元以来的秒数),根据需要执行到会话的time_zone转换:它代表一个精确,明确的时刻。 这就是你想要的; 只需确保适当地设置会话变量(使用SET SESSION time_zone = ... )。

有关更多信息,请参阅MySQL服务器时区支持 。

您可以存储到数据库之前将日期转换为UTC,然后在从数据库读取时转换回您自己的时区。

 long t = 1351382400000; // the timestamp in UTC String insert = "INSERT INTO my_table (timestamp) VALUES (?)"; PreparedStatement stmt = db.prepareStatement(insert); java.sql.Timestamp date = new Timestamp(t); stmt.setTimestamp(1, date); stmt.executeUpdate(); ..... TimeZone timezone = TimeZone.getTimeZone("MyTimeZoneId"); Calendar cal = java.util.Calendar.getInstance(timezone); String select = "SELECT timestamp FROM my_table"; // some code omitted.... ResultSet rs = stmt.executeQuery(); while (rs.next()) { java.sql.Timestamp ts = rs.getTimestamp(1); cal.setTimeInMillis(ts.getTime()); System.out.println("date in db: " + cal.getTime()); }