Почему я не могу получить временную метку даты «1914-11-08 00:00:00» в часовом поясе «Европа/Амстердам»?

Недавно я обновил свой MySQL-коннектор с 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

Исследуя эту ошибку, я понял, что новый драйвер MySQL выполнит некоторое преобразование, и мой набор данных содержит дату «1916-05-01 00:00 00». Согласно Википедии, первое общенациональное внедрение летнего времени было осуществлено Германской и Австро-Венгерской империями, начиная с 30 апреля 1916 года. Поэтому можно подумать, что оно не может перевести '1916-05-01 00:00 00» к метке времени, поскольку «1916-05-01 00:00 00» «не существует», только «1916-05-01 01:00 00». Я решил узнать все эти «несуществующие» даты в часовом поясе «Европа/Амстердам». Вот как я это сделал:

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-11-08»? В 1914 году летнего времени даже не существовало. Есть у кого-нибудь идеи?

Я хотел бы знать, почему я не могу получить временную метку даты «1914-11-08 00:00:00» в часовом поясе «Европа/Амстердам».

Можете ли вы вместо этого переключиться на использование новых классов java.time? Те, которые вы используете, полны ошибок и устарели.

Just another Java programmer 17.05.2024 09:54

Я знаю, что переход на классы java.time может решить эту проблему, но это было бы очень сложно сделать, поскольку это старое приложение, и с этим связана другая логика. Я просто не понимаю, почему это «1914-11-08 00:00:00» нельзя обработать. В 1914 году не было летнего времени, поэтому «1914-11-08 00:00:00» должно быть действительным временем.

Lawrence 17.05.2024 10:03

«TIMESTAMP» имеет диапазон от «1970-01-01 00:00:01» UTC до «2038-01-19 03:14:07» UTC». - dev.mysql.com/doc/refman/8.0/en/datetime.html вы имели в виду дату и время?

P.Salmon 17.05.2024 10:40

Я имею в виду java.sql.Timestamp. даже если время до '1970-01-01 00:00:01' UTC можно преобразовать в значение java.sql.Timestamp, верно? @P.Salmon

Lawrence 17.05.2024 11:48

Я воспроизвел. Мой результат error date: [1914-11-08, 1916-05-01].

Anonymous 17.05.2024 13:43

Моя Java 19 считает, что этот день начался в 1914-11-08T01:00+01:00[Europe/Amsterdam]. timeanddate.com не согласен.

Anonymous 17.05.2024 13:49

Если вы можете решить эту проблему, используя Instant или OffsetDateTime из java.time, я полагаю, что оно того стоит. Это не обязательно означает изменение всего кода даты и времени в исходных файлах Java. Для взаимодействия со старым кодом существуют простые методы преобразования.

Anonymous 18.05.2024 10:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
8
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Около двух лет назад создатели базы данных часовых поясов IANA решили, что, поскольку в Брюсселе и Амстердаме действуют одинаковые правила часовых поясов, начиная с 1970 года, они «свяжут» Европу/Амстердам с Европой/Брюсселем. Это фактически стерло историю правил часовых поясов до 1970 года для Европы / Амстердама и заменило правила Европы / Брюсселя до 1970 года.

Для Европы/Брюсселя местная дата 1914-11-08 00:00:00 не существует, поскольку в то время местное время было переведено на один час вперед до 1914-11-08 01:00:00. Это изменение не коснулось Амстердама.

Ваше обновление с 5.1.47 до 8.0.33, очевидно, уловило это изменение в базе данных IANA tz.


PS: Если вам не нравится это изменение в базе данных IANA tz, не срывайте это на мне. Я всего лишь посланник.

Другие вопросы по теме