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

时间:2017-03-28 14:18:30

标签: 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工作台中,它返回正确的日期时间。

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

显然这与英国的DST变化有关,该变化发生在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时,它会返回1am而不是凌晨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 个答案:

答案 0 :(得分:2)

这里有两个问题:

  1. 我们是否从服务器获取了所需的java.sql.Timestamp值?
  2. 当我们要求Java将Timestamp值显示为日期/时间字符串时,我们实际看到了什么?
  3. 为了帮助解决这个问题,我们可以以数字形式显示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格式的日期时间值与我们在SQL查询中使用CAST(dateTime AS CHAR)获得的字符串表示形式相匹配。