You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法获取Europe/Amsterdam时区下1914-11-08 00:00:00的Timestamp?

问题描述

将mysql-connector从5.1.47升级至8.0.33后,遇到如下错误:

Caused by: java.sql.SQLException: HOUR_OF_DAY: 0 -> 1
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:130) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:98) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:90) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:64) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:74) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:85) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.mysql.cj.jdbc.result.ResultSetImpl.getTimestamp(ResultSetImpl.java:947) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.mysql.cj.jdbc.result.ResultSetImpl.getTimestamp(ResultSetImpl.java:985) ~[mysql-connector-j-8.0.33.jar:8.0.33]
    at com.zaxxer.hikari.pool.HikariProxyResultSet.getTimestamp(HikariProxyResultSet.java) ~[HikariCP-4.0.3.jar:na]
    at org.hibernate.type.descriptor.sql.TimestampTypeDescriptor$2.doExtract(TimestampTypeDescriptor.java:84) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:47) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:257) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:243) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:329) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:3214) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.persister.entity.Loadable.hydrate(Loadable.java:94) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.loadFromResultSet(EntityReferenceInitializerImpl.java:342) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
    ... 113 common frames omitted

经排查,新版本驱动会进行时间转换,数据集中包含日期1916-05-01 00:00:00。根据资料,德国和奥匈帝国于1916年4月30日首次在全国推行夏令时,因此该时间属于“不存在”的时间,实际只有1916-05-01 01:00:00。

为了查找Europe/Amsterdam时区下所有这类“不存在”的日期,编写了以下代码:

public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        start.setTime(sdf.parse("1900-01-01"));
        end.setTime(sdf.parse("2025-01-01"));
        List<String> errorItems = new ArrayList<>();
        while (!start.after(end)) {
            try {
                Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Europe/Amsterdam"), Locale.US);
                calendar.setLenient(false);
                calendar.set(start.get(Calendar.YEAR), start.get(Calendar.MONTH), start.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
                Timestamp ts = new Timestamp(calendar.getTimeInMillis());
                System.out.println(ts);
            } catch (IllegalArgumentException e) {
                errorItems.add(sdf.format(start.getTime()));
            }
            start.add(Calendar.DAY_OF_YEAR, 1);
        }
        System.out.println("error date: " + errorItems);
    }

运行结果为:[1914-11-08, 1916-05-01]

已理解1916-05-01的问题,但1914年尚未推行夏令时,为何1914-11-08 00:00:00也无法获取Timestamp?

解答

这是因为Europe/Amsterdam时区在1914年11月8日存在临时时区调整:一战爆发后,荷兰在1914年10月1日开始实施了临时的“战争时间”(类似夏令时),并在11月8日凌晨将时钟从00:00直接拨快到01:00,跳过了00:00-01:00这个时间段。

虽然普遍认为荷兰首次正式夏令时是1916年,但1914年因战争实施了短期临时时间调整,Java使用的时区数据库(tzdata)记录了这段历史变更。当calendar.setLenient(false)开启严格模式时,不允许使用不存在的时间戳,因此1914-11-08 00:00:00会抛出IllegalArgumentException,和1916年的情况本质相同。

另外,mysql-connector 8.x版本默认使用更严格的时区转换逻辑,会严格校验时间有效性;而5.x版本的驱动对这类历史时区变更的处理更宽松,或者未同步最新的时区数据库,所以之前没有触发该错误。


内容的提问来源于stack exchange,提问作者Lawrence

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 03:06:01