Bug #50348 mysql connector/j 5.1.10 render the wrong value for dateTime column in GMT DB
Submitted: 14 Jan 2010 21:53 Modified: 20 Aug 2015 20:30
Reporter: shaoxian yang Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S1 (Critical)
Version:trunk OS:Any
Assigned to: Filipe Silva CPU Architecture:Any
Tags: dateTime column, DB in GMT zone, JVM timezone different

[14 Jan 2010 21:53] shaoxian yang
Description:
This problem happens in the following environment:

1. JVM timezone is America/Chicago by enforcing java user.timezone properties:
-Duser.timezone=America/Chicago

2. DB server is runngin in GMT timezone by using --default-time-zone=GMT.  DB server is running on linux and timezone table is prepopulate with whatever value from linux timezone files:   
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql

3. In SQL, Running a query which compares the DB dateTime column with a timestamp

mysql> SELECT @@global.time_zone, @@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| GMT                | GMT                 |
+--------------------+---------------------+
1 row in set (0.00 sec)

mysql> select ad_end_date from ad where id=1;
+---------------------+
| ad_end_date         |
+---------------------+
| 2010-12-20 23:59:59 |
+---------------------+
1 row in set (0.00 sec)

mysql> select * from ad where ad_end_date>='2010-12-21T00:00:01';
Empty set (0.00 sec)

In Java, Running the same query to compare the DB dateTime column with a java.sql.Timestamp.  However, this time, it will return non empty result, which should be wrong.

    private static Connection getConnection() {
        Connection conn = null;
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection("jdbc:mysql://10.37.2.24:3306/campaign_mgr?useTimezone=true&noTimezoneConversionForTimeType=true", "ad", "ad");
    }
    
    
    private static void getAdStartEndTimeByJDBC() {

            conn = getConnection();
            pst = conn.prepareStatement("select * from ad where id=1");

            rs = pst.executeQuery();

            while (rs.next()) {
                java.sql.Timestamp endDate = rs.getTimestamp("ad_end_date");
                System.out.println("endDate: " + endDate);
            }

    }
        private static void isDateInRangeByJdbc (java.sql.Timestamp timestamp) {

    
                conn = getConnection();
                
                
    
                pst = conn.prepareStatement("select * from ad where ad_end_date>=?");
    
                pst.setTimestamp(1, timestamp);
                rs = pst.executeQuery();
    
    
                while (rs.next()) {
                    adId = rs.getInt("id");
                    startDate = rs.getTimestamp("ad_start_date");
                    endDate = rs.getTimestamp("ad_end_date");
                }
                
        }
        
        
        
    private static void testDateTimeColumnInRange() {
        String dateTimeString = "2010-12-21T00:00:01.000";

        String timezone = "GMT";

        Timestamp timestamp = getTimestamp(dateTimeString, timezone);
        isDateInRangeByJdbc(timestamp);
    }
    
    
    private static Timestamp getTimestamp(String dateTimeString, String timezone) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
        sdf.setTimeZone(TimeZone.getTimeZone(timezone));

        java.util.Date date = null;

        try {
            date = sdf.parse(dateTimeString);

        } catch (ParseException pex) {
            logger_debug("unexpected Date parsingException: ", pex);
            throw new RuntimeException(pex);
        }

        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(timezone));
        cal.setTime(date);
        logger_debug("created calendar: " + cal);

        //java.sql.Timestamp timestamp = new java.sql.Timestamp(cal.getTimeInMillis());
        java.sql.Timestamp timestamp = new java.sql.Timestamp(date.getTime());
        return timestamp;

    }    
    
    
    
    
C:\temp\test>java -Duser.timezone=America/Chicago -classpath ".;.\c3p0-0.9.1.2.j
ar;.\mysql-connector-java-5.1.10-bin.jar" TimeZoneTest

===============  Print out some system property and default timezone ==================
userTimezone: America/Chicago

tz display name: Central Standard Time
tz id: America/Chicago

============  query Ad which id=1 using JDBC ==================
startDate: 2009-12-20 06:00:00.0
endDate: 2010-12-21 05:59:59.0

============ Create a java Calenar and java.sql.Timestamp based on "2010-12-21T00:00:01.000  GMT"
created calendar: java.util.GregorianCalendar[time=1292918401000,areFieldsSet=tr
ue,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="PST",of
fset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java
.util.SimpleTimeZone[id=PST,offset=-28800000,dstSavings=3600000,useDaylight=true
,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200
000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=720000
0,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONT
H=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=4,DAY_OF_MONTH=21,DAY_OF_YEAR=355,DAY_OF_WEEK
=3,DAY_OF_WEEK_IN_MONTH=3,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=1,MILLISE
COND=0,ZONE_OFFSET=-28800000,DST_OFFSET=0]
Created timestamp: 2010-12-21 02:00:01.0

================= isDateInRangeByJdbc ====================
adId: 1
adStartDate: 2009-12-20 06:00:00.0
adEndDate: 2010-12-21 05:59:59.0    
        
        
        
As you can see, the ad_end_date value printed by jdbc ("select * from ad where id=1") is wrong based on the JVM timezone already.  In DB, the dateTime column is "2010-12-20 23:59:59" based on GMT.  Therefore, the printout of java.sql.Timestamp based on JVM timezone (America/Chicago, wich is GMT-06:00) should be "2012-12-20 17:59:59".   However, as the program printout shows, it is not converting by GMT-06:00, instead it is doing GMT+06:00.  Maybe that can also explain why the jdbc query ("select * from ad where ad_end_date>=?") return the wrong result.

Strangely, when DB timezone is not GMT, e.g, but something like "America/Los_Angeles", then everything works fine.  I can see java.sql.Timestamp based on jvm timezone matches the moment described in DB timezone.

I think there is bug in terms of GMT.  Maybe logic to do plus or minus is messed up.

Thanks.

I will attach sample code as well.

How to repeat:
JVM timezone is America/Chicago

DB timezone is GMT

And then follow the description steps.

Suggested fix:
I think there is bug in terms of GMT.  Maybe logic to do plus or minus is messed up.
[14 Jan 2010 22:44] shaoxian yang
contains test file, sql scripts and executing scripts.

Attachment: bug_test.zip (application/x-zip-compressed, text), 3.52 KiB.

[29 Jan 2010 7:55] Tonci Grgin
Hi Shaoxian and thanks for your report.

These kind of things can be tricky to grasp as nothing's clear and straight forward. But I'll do my best.
[29 Jan 2010 12:03] Tonci Grgin
Test case for our test framework, working in JDK 1.5.

Attachment: TestBug50348.java (text/x-java), 4.00 KiB.

[29 Jan 2010 12:04] Tonci Grgin
Shaoxian, as I said, it took whole morning...

  o Set remote MySQL server to GMT
  o Start JVM with -Duser.timezone=America/Chicago on CET box (MySQL TZ id 92)

mysql> SELECT @@global.time_zone, @@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| GMT                | GMT                 |
+--------------------+---------------------+
				
mysql> select * from bug50348;
				+----+---------------------+---------------------+
| ID | AD_END_DATE         | CREATED_TIMESTAMP   |
+----+---------------------+---------------------+
|  1 | 2010-12-20 23:59:59 | 2010-01-29 10:27:09 |
+----+---------------------+---------------------+

Test case produces:
From DB end: 2010-12-21 05:59:59.0
From DB created: 2010-01-29 16:27:09.0
JVM converted TS for GMT: 2010-12-20 17:59:59.0

What puzzles me is this: I expected c/J to convert the date to same value as JVM does (see test case) but it's actually on the "other" side (+6). Now, this might not be a bug but intentional behavior. In any case it needs better documenting.

Mark?
[29 Jan 2010 18:43] shaoxian yang
Hi Toni,

It sounds like you and I are on the same page and getting the same test result.  Hopefully, we soon know what the reason behind this.

Thanks.

Shaoxian
[22 Jun 2010 0:04] Gisella Saavedra
Not just server in GMT.  java.sql.Timestamp, when retrieving such a field, has an internal value (long Epoch) incorrect.
[4 Feb 2011 14:28] Tonci Grgin
Using same test case against latest c/J sources still show a bug for me:
  o Set remote MySQL 5.1.31 to GMT, check the settings.
	+--------------------+---------------------+
	| @@global.time_zone | @@session.time_zone |
	+--------------------+---------------------+
	| GMT                | GMT                 |
	+--------------------+---------------------+
  o Set local (CET box) JVM to -Duser.region=US -Duser.language=en -Duser.timezone=America/Chicago
  o INSERT literal value into DB (2010-12-20T23:59:59.000). Get second value as current_timestamp from server (...`CREATED_TIMESTAMP` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP..., obviously in GMT)
  o Time of the test is around 15:00 CET (14:00 GMT, 08:00 CST)
  o Calculate JVM converted values for comparison:
	String tzdateTimeString =  "2010-12-20T23:59:59.000";
	String tzdateTimeString2 = "2011-02-04T14:50:50.000";
	String tzstring = "GMT";
				
	java.util.Date date = null;
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
	sdf.setTimeZone(TimeZone.getTimeZone(tzstring));
        date = sdf.parse(tzdateTimeString);

        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(tzstring));
        cal.setTime(date);
        java.sql.Timestamp tztimestamp = new java.sql.Timestamp(date.getTime());
        System.out.println("JVM converted TS for GMT: " + tztimestamp.toString());
				
        date = sdf.parse(tzdateTimeString2);
        cal.setTime(date);
        java.sql.Timestamp tztimestamp2 = new java.sql.Timestamp(date.getTime());
        System.out.println("JVM converted NOW TS for GMT: " + tztimestamp2.toString());

  o Select the data:

1) useTimezone, JDBC compliant shift and noTimezoneConversionForTimeType set to TRUE
    INSERTED DB value: 2010-12-20T23:59:59.000

    From DB, INSERTED value: 2010-12-20 23:59:59.0
    From DB created TS (server TZ, GMT!): 2011-02-04 13:59:48.0
    JVM converted TS for GMT: 2010-12-20 17:59:59.0
    JVM converted NOW TS for GMT: 2011-02-04 08:50:50.0

2) useTimezone and JDBC compliant shift set to TRUE, noTimezoneConversionForTimeType = false
INSERTED DB value: 2010-12-20T23:59:59.000

From DB, INSERTED value: 2010-12-20 23:59:59.0
From DB created TS (server TZ, GMT!): 2011-02-04 13:59:48.0
JVM converted TS for GMT: 2010-12-20 17:59:59.0
JVM converted NOW TS for GMT: 2011-02-04 08:50:50.0

3) useTimezone = true, JDBC compliant shift = false, noTimezoneConversionForTimeType = true
INSERTED DB value: 2010-12-20T23:59:59.000

From DB, INSERTED value: 2010-12-21 05:59:59.0
From DB created TS (server TZ, GMT!): 2011-02-04 19:58:01.0 <<< Here, 7 hours +
JVM converted TS for GMT: 2010-12-20 17:59:59.0
JVM converted NOW TS for GMT: 2011-02-04 08:50:50.0

4) useTimezone = true, JDBC compliant shift & noTimezoneConversionForTimeType  = false
INSERTED DB value: 2010-12-20T23:59:59.000

From DB, INSERTED value: 2010-12-21 05:59:59.0
From DB created TS (server TZ, GMT!): 2011-02-04 20:01:21.0
JVM converted TS for GMT: 2010-12-20 17:59:59.0
JVM converted NOW TS for GMT: 2011-02-04 08:50:50.0

5) ALL options OFF:
INSERTED DB value: 2010-12-20T23:59:59.000

From DB, INSERTED value: 2010-12-20 23:59:59.0
From DB created TS (server TZ, GMT!): 2011-02-04 14:03:32.0  << GMT value
JVM converted TS for GMT: 2010-12-20 17:59:59.0
JVM converted NOW TS for GMT: 2011-02-04 08:50:50.0
[20 Aug 2015 20:30] Daniel So
Added the following entry to the Connector/J 5.1.37 changelog:

"When the time zone on the MySQL server was configured to 'GMT' but the client was in a different time zone, Connector/J would make wrong adjustments for event timestamps when working with the server."