【问题标题】:Inserting date / time in a specific zone into MySQL database将特定区域中的日期/时间插入 MySQL 数据库
【发布时间】:2013-10-25 05:29:20
【问题描述】:

我发现无法在标准区域(如 UTC)中将日期/时间插入 MySQL 数据库。

以下代码段使用 Apache DBCP 对用户进行身份验证。

public final class AuthenticationDAO {

    public boolean isAuthenticated(String userName, String password) throws SQLException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        boolean status = false;

        try {
            connection = DatabaseConnection.getConnection();
            preparedStatement = connection.prepareStatement("select * from admin_tbl where lower(admin_id)=lower(?) and BINARY password=?");
            preparedStatement.setString(1, userName);
            preparedStatement.setString(2, password);
            resultSet = preparedStatement.executeQuery();
            status = resultSet.next();

            if (status) {
                preparedStatement = connection.prepareStatement("update admin_tbl set last_login=? where lower(admin_id)=lower(?) and BINARY password=?");
                preparedStatement.setTimestamp(1, new Timestamp(new Date().getTime())); // java.util.Date / java.sql.Timestamp
                preparedStatement.setString(2, userName);
                preparedStatement.setString(3, password);
                preparedStatement.executeUpdate();
            }
        } finally {
            if (connection != null) {connection.close();}
            if (resultSet != null) {resultSet.close();}
            if (preparedStatement != null) {preparedStatement.close();}
        }
        return status;
    }
}

此代码中只有一行是我们感兴趣的。

preparedStatement.setTimestamp(1, new Timestamp(new Date().getTime()));

当用户/管理员的登录尝试成功时,此行会更新登录日期/时间。 MySQL 中的列last_login 的类型为DATETIME


我想在 MySQL 的 UTC 区域中插入日期/时间,但这不会发生。看来它需要一个本地区域。

无论如何,是否可以将日期/时间插入 UTC 区域的 MySQL 中?可以建议 Java 和/或 MySQL 中的任何数据类型 - 不仅是我在这里介绍的。

【问题讨论】:

    标签: java mysql jdbc timezone utc


    【解决方案1】:

    您可以在两个地方为您的 mySQL 数据设置时区:全局配置和会话配置。您不能指定 mysql '这个日期是 GMT,但 这个 日期是 EST'。您必须将它们转换为通用时区。

    最佳做法是将您的服务器设置为特定时区 [例如:GMT],并​​确保您在存储期间相应地转换日期/时间。实际上,您的所有后端数据都应该使用一个时区,并且任何转换都应该在输出之前立即进行。

    【讨论】:

      【解决方案2】:

      Date 在 Java 中与时区无关。它始终采用 UTC(默认情况下且始终如此)。因此,DateTimestamp 本身不是原因,而是当 Date / Timestamp 通过 JDBC 驱动程序传递到数据库时,它根据默认为系统时间的 JVM 时区解释日期/时间依次是区域(本机操作系统区域)。

      因此,除非 MySQL JDBC 驱动程序被显式强制使用 UTC 区域或 JVM 本身设置为使用该区域,否则它不会使用 UTC 将 Date / Timestamp 存储到目标数据库中,即使 MySQL 本身配置为使用 UTC 在 my.ini 中使用 default_time_zone='+00:00' 或在 [mysqld] 部分中使用 my.cnf。像 Oracle 这样的一些数据库可能支持带时区的时间戳,这可能是我不熟悉的一个例外(未经测试,因为我目前没有那个环境)。

      void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException

      将指定参数设置为给定的java.sql.Timestamp 值, 使用给定的日历对象。驱动程序使用日历对象 构造一个 SQL TIMESTAMP 值,然后驱动程序将其发送到 数据库。使用 Calendar 对象,驱动程序可以计算 考虑到自定义时区的时间戳。 如果没有Calendar 对象 指定时,驱动程序使用默认时区,即 运行应用程序的虚拟机

      java.sql.PreparedStatement 类中处理日期时间的其他相关 getter 和 setter 方法也提到了同样的事情。

      这可以通过检查 MySQL JDBC 驱动程序实现的setTimestampInternal() 方法的调用来进一步澄清。

      setTimestamp() 方法的两个重载版本中查看以下twosetTimestampInternal() 方法的调用。

      /**
       * Set a parameter to a java.sql.Timestamp value. The driver converts this
       * to a SQL TIMESTAMP value when it sends it to the database.
       *
       * @param parameterIndex the first parameter is 1...
       * @param x the parameter value
       *
       * @throws SQLException if a database access error occurs
       */
      public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
          setTimestampInternal(parameterIndex, x, this.connection.getDefaultTimeZone());
      }
      
      /**
       * Set a parameter to a java.sql.Timestamp value. The driver converts this
       * to a SQL TIMESTAMP value when it sends it to the database.
       *
       * @param parameterIndex the first parameter is 1, the second is 2, ...
       * @param x the parameter value
       * @param cal the calendar specifying the timezone to use
       *
       * @throws SQLException if a database-access error occurs.
       */
      public void setTimestamp(int parameterIndex, java.sql.Timestamp x,Calendar cal) throws SQLException {
          setTimestampInternal(parameterIndex, x, cal.getTimeZone());
      }
      

      因此,当没有使用 PreparedStatement#setTimestamp() 方法指定 Calendar 实例时,将使用默认时区 (this.connection.getDefaultTimeZone()) 或使用提供的 Calendar 实例的指定时区 (cal.getTimeZone()) .

      因此,只需将java.util.Calendar 或其子类java.util.GregorianCalendar 的实例传递给PreparedStatement#setTimestamp(),并使用您感兴趣的适当时区(我们感兴趣的UTC)。

      Timestamp timestamp = new Timestamp(new java.util.Date().getTime());
      Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
      
      preparedStatement.setTimestamp(1, timestamp, calendar);
      

      指定的时区将用于生成Timestamp 的字符串表示形式,然后再将其传递给底层数据库。


      如果您碰巧处理了诸如 Hibernate、EclipseLink 之类的 ORM 框架以及使用适当的数据源在应用程序服务器/Servlet 容器中创建连接池,请参阅此答案基于并直接复制的 this answer来自。

      【讨论】:

        猜你喜欢
        • 2013-04-27
        • 2012-05-21
        • 2019-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多