Bug #69545 Conversion of Time field data to Timestamp type may produce incorrect result
Submitted: 21 Jun 2013 20:15 Modified: 26 Jun 2013 15:50
Reporter: Lawrenty Novitsky Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / ODBC Severity:S3 (Non-critical)
Version:5.2.5 OS:Any
Assigned to: Lawrenty Novitsky CPU Architecture:Any
Tags: time timestamp

[21 Jun 2013 20:15] Lawrenty Novitsky
Description:
According to specs(http://msdn.microsoft.com/en-us/library/windows/desktop/ms712436%28v=vs.85%29.aspx) if TIME sql data converted to TIMESTAMP,  date fields of the timestamp structure are set to the current date, and the fraction field is set to null. But if value of time field >= 24h, the result timestamp structure will contain incorrect timestamp. Besides fraction field of that structure won't be reset.

How to repeat:
DECLARE_TEST(t_bugtime2ts)
{
  SQL_TIMESTAMP_STRUCT ts;
  SQLLEN outlen= 0;

  /* make sure we have reset everything to zero */
  memset(&ts, 0, sizeof(SQL_TIMESTAMP_STRUCT));

  /* check situations with sec and min overflow */
  ok_sql(hstmt, "SELECT CAST('24:45:07.0001' AS TIME(6))");
  ok_stmt(hstmt, SQLFetch(hstmt));

  ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_TIMESTAMP, &ts, sizeof(ts), &outlen));
  is_num(outlen, sizeof(ts));
  /* hour cannot go out of unsigned smallint range */
  is_num(ts.hour, 0);
  is_num(ts.minute, 45);
  is_num(ts.second, 7);
  /* Fractional seconds must be 0 no matter what is actually in the field */
  is_num(ts.fraction, 0);

  {
	/* We need tomorrow date */
    time_t t= time(NULL) + 24*60*60;
    struct tm *cur_tm= localtime(&t);

	is_num(ts.year,  1900 + cur_tm->tm_year);
	is_num(ts.month, 1    + cur_tm->tm_mon);
	is_num(ts.day,   cur_tm->tm_mday);
  }

  ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE));
  return OK;  
}

Suggested fix:
=== modified file 'driver/results.c'
--- driver/results.c    2013-04-22 11:46:11 +0000
+++ driver/results.c    2013-06-21 20:07:43 +0000
@@ -573,6 +573,13 @@
             (SQL_TIMESTAMP_STRUCT *)rgbValue;
           time_t sec_time= time(NULL);
           struct tm cur_tm;
+
+                 if (ts.hour > 23)
+                 {
+                   sec_time+= (ts.hour/24)*24*60*60;
+                       ts.hour= ts.hour%24;
+                 }
+
           localtime_r(&sec_time, &cur_tm);

           /* I wornder if that hasn't to be server current date*/
@@ -582,7 +589,8 @@
           timestamp_info->hour=   ts.hour;
           timestamp_info->minute= ts.minute;
           timestamp_info->second= ts.second;
-          get_fractional_part(tmp, SQL_NTS, TRUE, &timestamp_info->fraction);
+                 /* Fractional seconds must be 0 no matter what is actually in the field */
+          timestamp_info->fraction= 0;
           *pcbValue= sizeof(SQL_TIMESTAMP_STRUCT);
         }
       }
[21 Jun 2013 20:22] Lawrenty Novitsky
the patch

Attachment: bug69545.patch (text/x-patch), 2.30 KiB.

[26 Jun 2013 15:50] Lawrenty Novitsky
The patch has been pushed as rev#1153
[25 Jul 2013 18:19] Daniel So
Added an entry to the Connector/ODBC 5.2.6 changelog:

When the SQL TIME data was converted to the C data type of TIMESTAMP, the date fields did not get set to the current date and the fraction field for seconds did not get set to zero as required by the ODBC specifications when the value of the hour field is greater than 24.