java - MySQL选择没有夏令时更改的日期时间

标签 java mysql jdbc

我有一张表,用于存储来自传感器的数据

CREATE TABLE `testdatabase` (
  `dateTime` datetime DEFAULT NULL,
  `data` varchar(200) DEFAULT NULL,
  `sensorID` varchar(10) DEFAULT NULL
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6918 DEFAULT CHARSET=utf8

这是我正在运行的选择查询,

SELECT `dateTime` from testdatabase WHERE sensorID='ABC1234' AND (dateTime BETWEEN '2017-03-26 00:01:00' AND '2017-03-26 00:00:00') order by `dateTime` asc;

当我在 MySQL Workbench 中运行此查询时,它返回正确的日期时间。

2017-03-26 00:10:00
2017-03-26 00:20:00
2017-03-26 00:30:00
2017-03-26 00:40:00
2017-03-26 00:50:00
2017-03-26 01:00:00
2017-03-26 01:10:00
2017-03-26 01:20:00
2017-03-26 01:30:00
2017-03-26 01:40:00
2017-03-26 01:50:00
2017-03-26 02:00:00
2017-03-26 02:10:00
2017-03-26 02:20:00
2017-03-26 02:30:00
2017-03-26 02:40:00
2017-03-26 02:50:00
2017-03-26 03:00:00

但是当我从我的 java 应用程序运行这个查询时,它返回以下日期时间。

2017-03-26 00:10:00
2017-03-26 00:20:00
2017-03-26 00:30:00
2017-03-26 00:40:00
2017-03-26 00:50:00
2017-03-26 02:00:00
2017-03-26 02:10:00
2017-03-26 02:20:00
2017-03-26 02:30:00
2017-03-26 02:40:00
2017-03-26 02:50:00
2017-03-26 02:00:00
2017-03-26 02:10:00
2017-03-26 02:20:00
2017-03-26 02:30:00
2017-03-26 02:40:00
2017-03-26 02:50:00
2017-03-26 03:00:00

显然,这与英国 3 月 26 日凌晨 1 点发生的夏令时变化有关。 我认为 JDBC 连接正在将时间更改为 BST,但我不希望这样。

我尝试将 MySQL 服务器全局时区设置为 00:00

SET @@global.time_zone='+00:00';

但是没有成功! 如何在不更改 DST 的情况下获得正确的时间?

编辑: 我正在使用 Java 1.7mysql-connector-java-5.1.19-bin.jar

我在 Mark Rotteveel 评论之前但之后以字符串形式获取数据 我尝试了 PreparedStaement 并将日历对象设置为 UTC,但两者都返回相同的结果。

try 
{
    Statement statement = connection.createStatement();
    ResultSet resultSet = statement.executeQuery(query);
    while(resultSet.next())
    {
        System.out.println("Without Calendar Object : "+resultSet.getString(1));
    }
}catch (SQLException e){e.printStackTrace();}

try
{
    PreparedStatement statement = connection.prepareStatement(query);
    ResultSet resultSet = statement.executeQuery();
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    while(resultSet.next())
    {
        System.out.println("With Calendar Object : "+resultSet.getTimestamp(1, cal));
    }
}catch (SQLException e){e.printStackTrace();}

OUTPUT:
    Without Calendar Object : 2017-03-26 00:10:00.0
    Without Calendar Object : 2017-03-26 00:20:00.0
    Without Calendar Object : 2017-03-26 00:30:00.0
    Without Calendar Object : 2017-03-26 00:40:00.0
    Without Calendar Object : 2017-03-26 00:50:00.0
    Without Calendar Object : 2017-03-26 02:00:00.0
    Without Calendar Object : 2017-03-26 02:10:00.0
    Without Calendar Object : 2017-03-26 02:20:00.0
    Without Calendar Object : 2017-03-26 02:30:00.0
    Without Calendar Object : 2017-03-26 02:40:00.0
    Without Calendar Object : 2017-03-26 02:50:00.0
    Without Calendar Object : 2017-03-26 02:00:00.0
    Without Calendar Object : 2017-03-26 02:10:00.0
    Without Calendar Object : 2017-03-26 02:20:00.0
    Without Calendar Object : 2017-03-26 02:30:00.0
    Without Calendar Object : 2017-03-26 02:40:00.0
    Without Calendar Object : 2017-03-26 02:50:00.0
    Without Calendar Object : 2017-03-26 03:00:00.0
    With Calendar Object : 2017-03-26 00:10:00.0
    With Calendar Object : 2017-03-26 00:20:00.0
    With Calendar Object : 2017-03-26 00:30:00.0
    With Calendar Object : 2017-03-26 00:40:00.0
    With Calendar Object : 2017-03-26 00:50:00.0
    With Calendar Object : 2017-03-26 02:00:00.0
    With Calendar Object : 2017-03-26 02:10:00.0
    With Calendar Object : 2017-03-26 02:20:00.0
    With Calendar Object : 2017-03-26 02:30:00.0
    With Calendar Object : 2017-03-26 02:40:00.0
    With Calendar Object : 2017-03-26 02:50:00.0
    With Calendar Object : 2017-03-26 02:00:00.0
    With Calendar Object : 2017-03-26 02:10:00.0
    With Calendar Object : 2017-03-26 02:20:00.0
    With Calendar Object : 2017-03-26 02:30:00.0
    With Calendar Object : 2017-03-26 02:40:00.0
    With Calendar Object : 2017-03-26 02:50:00.0
    With Calendar Object : 2017-03-26 03:00:00.0

编辑 2: 还有一件奇怪的事情,当我将默认时区更改为 UTC 时,它会返回凌晨 1 点而不是凌晨 2 点的重复值。

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

输出:

Without Calendar Object : 2017-03-26 00:10:00.0
Without Calendar Object : 2017-03-26 00:20:00.0
Without Calendar Object : 2017-03-26 00:30:00.0
Without Calendar Object : 2017-03-26 00:40:00.0
Without Calendar Object : 2017-03-26 00:50:00.0
Without Calendar Object : 2017-03-26 01:00:00.0
Without Calendar Object : 2017-03-26 01:10:00.0
Without Calendar Object : 2017-03-26 01:20:00.0
Without Calendar Object : 2017-03-26 01:30:00.0
Without Calendar Object : 2017-03-26 01:40:00.0
Without Calendar Object : 2017-03-26 01:50:00.0
Without Calendar Object : 2017-03-26 01:00:00.0
Without Calendar Object : 2017-03-26 01:10:00.0
Without Calendar Object : 2017-03-26 01:20:00.0
Without Calendar Object : 2017-03-26 01:30:00.0
Without Calendar Object : 2017-03-26 01:40:00.0
Without Calendar Object : 2017-03-26 01:50:00.0
Without Calendar Object : 2017-03-26 02:00:00.0
With Calendar Object : 2017-03-26 00:10:00.0
With Calendar Object : 2017-03-26 00:20:00.0
With Calendar Object : 2017-03-26 00:30:00.0
With Calendar Object : 2017-03-26 00:40:00.0
With Calendar Object : 2017-03-26 00:50:00.0
With Calendar Object : 2017-03-26 01:00:00.0
With Calendar Object : 2017-03-26 01:10:00.0
With Calendar Object : 2017-03-26 01:20:00.0
With Calendar Object : 2017-03-26 01:30:00.0
With Calendar Object : 2017-03-26 01:40:00.0
With Calendar Object : 2017-03-26 01:50:00.0
With Calendar Object : 2017-03-26 01:00:00.0
With Calendar Object : 2017-03-26 01:10:00.0
With Calendar Object : 2017-03-26 01:20:00.0
With Calendar Object : 2017-03-26 01:30:00.0
With Calendar Object : 2017-03-26 01:40:00.0
With Calendar Object : 2017-03-26 01:50:00.0
With Calendar Object : 2017-03-26 02:00:00.0

最佳答案

这里有两个问题:

  1. 我们是否从服务器返回了所需的 java.sql.Timestamp 值?
  2. 当我们要求 Java 将 Timestamp 值显示为日期/时间字符串时,我们实际上看到了什么?

为了帮助解决这个问题,我们可以以数字形式显示 Timestamp 值。 Timestamp#getTime() 返回自纪元 (1970-01-01 00:00:00 UTC) 以来的毫秒数,但如果我们将其除以 60000 可以使其更易于阅读给出纪元以来的分钟数。

我们还可以使用 SimpleDateFormat 对象以明确的方式格式化 Timestamp 值。

因此对于名为 tztest 的示例表,在 phpMyAdmin 中看起来像这样

id  dateTime
--  -------------------
 1  2017-03-26 00:10:00
 2  2017-03-26 01:10:00
 3  2017-03-26 02:10:00

当我运行以下 Java 代码时

connectionUrl = "jdbc:mysql://localhost:3307/mydb";
try (Connection conn = DriverManager.getConnection(connectionUrl, myUid, myPwd)) {
    SimpleDateFormat sdfLocal = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
    Calendar calUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    SimpleDateFormat sdfUTC = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
    sdfUTC.setCalendar(calUTC);
    String sql = 
            "SELECT id, CAST(dateTime AS CHAR) AS datetimeString, dateTime " + 
            "FROM tztest ORDER BY dateTime";
    try (   Statement st = conn.createStatement();
            ResultSet rs = st.executeQuery(sql)) {
        while (rs.next()) {
            int id = rs.getInt("id");
            String datetimeString = rs.getString("datetimeString");
            Timestamp timestampValue = rs.getTimestamp("dateTime", calUTC);
            long minutesSinceEpoch = timestampValue.getTime() / 60000;
            System.out.printf("%d: %s -> %d minutes since epoch -> %s / %s%n",
                    id,
                    datetimeString, 
                    minutesSinceEpoch,
                    sdfLocal.format(timestampValue),
                    sdfUTC.format(timestampValue)
                    );
        }
    }
} catch (Exception e) {
    e.printStackTrace(System.err);
}

控制台输出是

1: 2017-03-26 00:10:00 -> 24841450 minutes since epoch -> 2017-03-26 00:10:00 GMT / 2017-03-26 00:10:00 UTC
2: 2017-03-26 01:10:00 -> 24841510 minutes since epoch -> 2017-03-26 02:10:00 BST / 2017-03-26 01:10:00 UTC
3: 2017-03-26 02:10:00 -> 24841510 minutes since epoch -> 2017-03-26 02:10:00 BST / 2017-03-26 01:10:00 UTC

很明显,我没有得到所需的 Timestamp 值,因为第 2 行和第 3 行都显示“自纪元以来 24841510 分钟”。

我可以通过简单地将 useLegacyDatetimeCode=false 添加到我的连接字符串来纠正这个问题

connectionUrl = "jdbc:mysql://localhost:3307/mydb?useLegacyDatetimeCode=false";

之后控制台输出是

1: 2017-03-26 00:10:00 -> 24841450 minutes since epoch -> 2017-03-26 00:10:00 GMT / 2017-03-26 00:10:00 UTC
2: 2017-03-26 01:10:00 -> 24841510 minutes since epoch -> 2017-03-26 02:10:00 BST / 2017-03-26 01:10:00 UTC
3: 2017-03-26 02:10:00 -> 24841570 minutes since epoch -> 2017-03-26 03:10:00 BST / 2017-03-26 02:10:00 UTC

请注意,第 3 行现在显示“自纪元以来 24841570 分钟”,比第 2 行晚 60 分钟。另请注意,UTC 格式的日期时间值与我们使用 CAST(dateTime AS CHAR)< 获得的字符串表示相匹配 在 SQL 查询中。

关于java - MySQL选择没有夏令时更改的日期时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43072071/

相关文章:

hadoop - 无法在Hortonworks Sandbox 2.0中通过JDBC访问Hive表

java - 单元格选择后更新数据库(JTable)

java - java关闭连接怎么写?

java - 在上传到服务器之前检查文件内容

java - Jackson 如何将一个 Pojo 字段映射到 2 个(json)字段(相同的内容,不同的名称)

java - Android GUI 不显示

mysql - 如何确保 HAVING 发生在 GROUP BY 之前

java - 当 maven build 运行测试时,Spring boot 项目抛出 *$$EnhancerBySpringCGLIB is not an @AspectJ Aspect

mysql - 当 View 中的数据发生变化时触发查询

php - 通过php导出到excel文件时如何保持mysql数据的格式