Bug #82005 JdbcDateValueFactory fails to parse some dates
Submitted: 26 Jun 2016 9:09 Modified: 9 Mar 2017 15:17
Reporter: Alexandru-Constantin Bledea Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S1 (Critical)
Version:6.0.3 OS:Ubuntu
Assigned to: CPU Architecture:Any

[26 Jun 2016 9:09] Alexandru-Constantin Bledea
Description:
The 6.x drivers don't work when parsing some dates

The column definition is simply "date NOT NULL"
I can select where the date = '1994-03-27' and i get columns, however, when it comes to them getting mapped, the driver fails

The stacktrace is

Caused by: java.lang.IllegalArgumentException: HOUR_OF_DAY: 0 -> 1
	at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
	at java.util.Calendar.updateTime(Calendar.java:3393)
	at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
	at com.mysql.cj.jdbc.io.JdbcDateValueFactory.createFromDate(JdbcDateValueFactory.java:66)
	at com.mysql.cj.jdbc.io.JdbcDateValueFactory.createFromDate(JdbcDateValueFactory.java:39)
	at com.mysql.cj.core.io.BaseDecoratingValueFactory.createFromDate(BaseDecoratingValueFactory.java:46)
	at com.mysql.cj.core.io.BaseDecoratingValueFactory.createFromDate(BaseDecoratingValueFactory.java:46)
	at com.mysql.cj.core.io.MysqlTextValueDecoder.decodeDate(MysqlTextValueDecoder.java:67)
	at com.mysql.cj.jdbc.ResultSetRow.decodeAndCreateReturnValue(ResultSetRow.java:113)
	at com.mysql.cj.jdbc.ResultSetRow.getValueFromBytes(ResultSetRow.java:269)
	at com.mysql.cj.jdbc.ByteArrayRow.getValue(ByteArrayRow.java:89)
	at com.mysql.cj.jdbc.ResultSetImpl.getNonStringValueFromRow(ResultSetImpl.java:813)
	at com.mysql.cj.jdbc.ResultSetImpl.getDateOrTimestampValueFromRow(ResultSetImpl.java:826)
	at com.mysql.cj.jdbc.ResultSetImpl.getDate(ResultSetImpl.java:971)
	at com.mysql.cj.jdbc.ResultSetImpl.getDate(ResultSetImpl.java:982)
	at org.hibernate.type.descriptor.sql.DateTypeDescriptor$2.doExtract(DateTypeDescriptor.java:74)
	at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:64)
	at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:267)
	at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:263)
	at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253)
	at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:338)
	at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2969)
	at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1696)
	at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1628)
	at org.hibernate.loader.Loader.getRow(Loader.java:1515)
	at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:726)
	at org.hibernate.loader.Loader.processResultSet(Loader.java:953)
	at org.hibernate.loader.Loader.doQuery(Loader.java:921)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:355)
	at org.hibernate.loader.Loader.doList(Loader.java:2554)
	at org.hibernate.loader.Loader.doList(Loader.java:2540)
	at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2370)
	at org.hibernate.loader.Loader.list(Loader.java:2365)
	at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:497)
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:387)
	at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:236)
	at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1300)
	at org.hibernate.internal.QueryImpl.list(QueryImpl.java:103)
	at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:573)
	at org.hibernate.jpa.internal.QueryImpl.getResultList(QueryImpl.java:449)
	at org.hibernate.jpa.criteria.compile.CriteriaQueryTypeQueryAdapter.getResultList(CriteriaQueryTypeQueryAdapter.java:67)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:323)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

How to repeat:
using mysql classes:

    public static void main(String[] args) {
        new JdbcDateValueFactory(TimeZone.getTimeZone("Europe/Bucharest")).createFromDate(1994, 3, 27);
    }

plain java:

    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Bucharest"), Locale.US);
        cal.set(MILLISECOND, 0);
        cal.set(0, 0, 0, 0, 0);
        cal.setLenient(false); // hmmm...
        cal.set(1994, MARCH, 27);
        long ms = cal.getTimeInMillis(); // boom!
    }

Suggested fix:
leaving the calendar as lenient
[26 Jun 2016 9:12] Alexandru-Constantin Bledea
The plain java version is actually

        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Bucharest"), Locale.US);
        cal.set(MILLISECOND, 0);
        cal.setLenient(false);
        cal.getTimeInMillis();
        cal.set(1994, MARCH, 27, 0, 0, 0);
        long ms = cal.getTimeInMillis();

I don't know how to edit the initial submission
[27 Jun 2016 12:06] Chiranjeevi Battula
Hello Alexandru-Constantin Bledea,

Thank you for the bug report and test case.
Verified as described.

Thanks,
Chiranjeevi.
[27 Jun 2016 12:11] Chiranjeevi Battula
Exception in thread "main" java.lang.IllegalArgumentException: HOUR_OF_DAY: 0 -> 1
	at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
	at java.util.Calendar.updateTime(Calendar.java:3393)
	at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
	at com.mysql.cj.jdbc.io.JdbcDateValueFactory.createFromDate(JdbcDateValueFactory.java:66)
	at javaapplication3.bug_82005.main(bug_82005.java:32)
C:\Users\cbattula\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 0 seconds)
[28 Sep 2016 7:48] Alexandru-Constantin Bledea
SimpleDateFormat can handle this date even if is set as not lenient

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", US);
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Bucharest"));
sdf.setLenient(false);
System.out.println(sdf.parse("1994-03-27"));
[31 Jan 2017 12:02] Alexander Rumyantsev
It also can be reproduced in 6.0.5 version.
[9 Mar 2017 15:17] Daniel So
Posted by developer:
 
Added the following entry to the Connector/J 6.0.6 changelog: 

"An IllegalArgumentException was thrown when lenient was false for a Calendar object and the hours, minutes, and seconds explicitly set via the Calendar constructor did not match the values resulted from the timezone conversion of the Calendar object's date value."
[26 Feb 2019 19:06] Eduardo Simioni
I know this issue has been closed, but this comment might save time for some people.

The problem in my case was occurring given the following scenario:

- A column with type DATE in a table, with value '2018-11-04'.
- DB Server on AWS/RDS with timezone UTC
- JDBC connection URL with parameter: serverTimezone=America/Sao_Paulo
- Connector/J version 8.0.15
- JVM OpenJDK Runtime Environment (build 1.8.0_191-b12) running in a Linux server with timezone America/Sao_Paulo

It happens that '2018-11-04 00:00:00' was the start of summer time for the mentioned time zone, which actually makes '2018-11-04 00:00:00' not exist, since it changes to '2018-11-04 01:00:00'.

Note that the column type is DATE, so, there is no time information, but it is mapped to a Calendar in my model.

When MySQL was trying to convert, it was throwing an exception:

Caused by: java.lang.IllegalArgumentException: HOUR_OF_DAY: 0 -> 1
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at com.mysql.cj.result.SqlTimestampValueFactory.createFromTimestamp(SqlTimestampValueFactory.java:100)

The workaround that seems to work best is:

- Remove serverTimezone from JDBC connection URL
- Add hibernate.jdbc.time_zone configuration set to America/Sao_Paulo
[30 May 2019 18:46] Joel Salmerón Viver
Also for information to others:

We also ran into the America/Sao_Paulo invalid timezone dates due to an error by our sysadmins of not patching the the os in time; hence we received this error running a Debezium kafka connector which uses jdbc to read the bin_log api.

We had to update the invalid timestamp rows to add an hour (those timestamps from 2018-11-04 00:00 to 2018-11-04 0:59), and then also the version of the java jdk (8u101) the kconnect workers were using; upgrading (to 8u172 - tzdata2018c 

REF: https://www.oracle.com/technetwork/cn/java/javase/downloads/tzdata-versions-138805.html
[12 May 2020 10:34] Surj Basan
Firstly, I just wanted to thank the previous commenters for pointing out the timezone issues.

Secondly, this is how I resolved my own issue with this bug: simply by adding "default_time_zone = UTC" to local my.conf. The server already had it, but my local one didn't. Apparently, this made MySQL encounter impossible dates and it threw the exception logged in this ticket.