如何将java.util.Date存储到UTC / GMT时区的MySQL时间戳字段中?

我使用了一个新的Date()对象来填充MySQL DB中的字段,但是该字段中存储的实际值是在我的本地时区。

如何配置MySQL以将其存储在UTC / GMT时区?

我认为,配置连接字符串会有所帮助,但我不知道如何。 连接字符串中有许多属性,如useTimezone,serverTimzone,useGmtMillisForDatetimes,useLegacyDatetimeCode,…

简短的回答是:

  • 将“default-time-zone = utc”添加到my.cnf
  • 在您的代码中,始终以UTC“思考”,除非为您的用户显示日期
  • 使用JDBC获取/设置日期或时间戳时,请始终使用Calendar参数,设置为UTC:

    resultset.getTimestamp(“my_date”,Calendar.getInstance(TimeZone.getTimeZone(“UTC”)));

  • 要么将服务器与NTP同步,要么仅依靠数据库服务器告诉您它的时间。

答案很长就是这样:

在处理任何数据库中的日期和时区以及任何客户端代码时,我通常建议采用以下策略:

  1. 将数据库配置为使用UTC时区 ,而不是使用服务器的本地时区(当然,除非它是UTC)。

    • 如何执行此操作取决于您的数据库服务器。 有关MySQL的说明,请访问: http : //dev.mysql.com/doc/refman/5.0/en/time-zone-support.html 。 基本上你需要在my.cnf中写这个:default-time-zone = utc

    • 这样,您可以在任何地方托管数据库服务器,轻松更改托管位置,以及更一般地操作服务器上的日期而不会产生任何歧义。

    • 如果您真的更喜欢使用本地时区,我建议至少关闭夏令时,因为数据库中的日期不明确可能是一场真正的噩梦。
      • 例如,如果您正在构建电话服务并且您在数据库服务器上使用夏令时,那么您就会遇到麻烦:没有办法判断是否有来自“2008-10-26 02:30”的客户:00“到”2008-10-26 02:35:00“实际上要求5分钟或1小时5分钟(假设夏令时发生在10月26日凌晨3点)!
  2. 在应用程序代码中,始终使用UTC日期,除非向用户显示日期。

    • 在Java中,从数据库中读取时,始终使用:

    时间戳myDate = resultSet.getTimestamp(“my_date”,Calendar.getInstance(TimeZone.getTimeZone(“UTC”)));

    • 如果不这样做,则假定时间戳位于本地TimeZone中,而不是UTC。
  3. 同步服务器或仅依赖数据库服务器的时间

    • 如果您的Web服务器在一台服务器(或更多)上,而您的数据库服务器在其他服务器上,那么我强烈建议您将它们的时钟与NTP同步。

    • 或者,只依靠一台服务器告诉你它的时间。 通常,数据库服务器是最好的时间。 换句话说,避免使用如下代码:

    preparedStatement = connection.prepareStatement(“UPDATE my_table SET my_time =?WHERE […]”);
    java.util.Date now = new java.util.Date(); // 当地时间! 🙁
    preparedStatement.setTimestamp(1,new Timestamp(now.getTime()));
    int result = preparedStatement.execute();

    • 相反,依赖数据库服务器的时间:

    preparedStatement = connection.prepareStatement(“UPDATE my_table SET my_time = NOW()WHERE […]”);
    int result = preparedStatement.execute();

希望这可以帮助! 🙂

一般来说,MiniQuark为数据库提供了一些很好的答案,但是有一些特定的MySql需要考虑……

配置数据库以使用UTC时区

这实际上不足以解决问题。 如果您在OP询问时将java.util.Date传递给MySql,则MySql驱动程序将更改该值 ,使其看起来与数据库时区中的本地时间相同。

示例:如果配置为UTC,则为数据库。 您的申请是EST。 您传递java.util.Date对象5:00(EST)。 数据库将其转换为5:00 UTC并存储它。 真棒。

在传递数据之前,您必须调整时间以“撤消”此自动调整。 就像是…

long originalTime = originalDate.getTime(); Date newDate = new Date(originalTime - TimeZone.getDefault().getOffset(originalTime)); ps.setDate(1, newDate); 

读回数据需要进行类似的转换..

 long dbTime = rs.getTimestamp(1).getTime(); Date originalDate = new Date(dbTime + TimeZone.getDefault().getOffset(dbTime)); 

这是另一个有趣的怪癖……

在Java中,从数据库读取时,始终使用:Timestamp myDate = resultSet.getTimestamp(“my_date”,Calendar.getInstance(TimeZone.getTimeZone(“UTC”)));

MySql实际上忽略了Calendar参数。 无论您传递的是哪个日历,都会返回相同的值。

我遇到了同样的问题,我花了将近一天的时间来追踪。 我在MySQL中存储DateTime列。 在Amazon的云中运行的RDS实例默认设置为具有UTC时间戳。

Buggy代码是:

  String startTime = "2013-02-01T04:00:00.000Z"; DateTime dt = ISODateTimeFormat.dateTimeParser().parseDateTime(startTime); PreparedStatement stmt = connection.prepareStatement(insertStatementTemplate); Timestamp ts = new Timestamp(dt.getMillis()); stmt.setTimestamp(1, ts, Calendar.getInstance(TimeZone.getTimeZone("UTC"))); 

在上面的代码中,“。setTimestamp”调用不会将日期作为UTC日期!

经过几个小时的调查,结果certificate这是Java / MySQL驱动程序中的一个已知错误。 对setTimestamp的调用只是忽略了Calendar参数。

要解决此问题,请将“ useLegacyDatetimeCode = false ”添加到数据库URI中。

 private final static String DatabaseName = "jdbc:mysql://foo/?useLegacyDatetimeCode=false"; 

一旦我这样做,存储在MySQL数据库中的日期是以正确的UTC格式,而不是在我本地工作站的时区。

好吧,如果我们正在谈论使用PreparedStatement ,那么有一种setDateforms ,您可以将Calendar设置传递到特定时区。

例如,如果您有一个名为stmt的PreparedStatement,一个名为date的Date,并假设date是第二个参数:

 stmt.setDate(2, date, Calendar.getInstance(TimeZone.getTimeZone("GMT"))); 

最好的部分是,即使GMT是无效的时区名称,由于getTimeZone的默认行为,它仍然会返回GMT时区。

Java Date与时区无关。 它始终表示距离Epoch的GMD(UTC)日期(以毫秒为单位)。

时区相关的唯一时间是您将日期作为字符串发布或将数据字符串解析为日期对象。