List:Internals« Previous MessageNext Message »
From:Jim Winstead Date:October 14 2005 12:32am
Subject:bk commit into 5.0 tree (jimw:1.2024) BUG#13534
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of jimw. When jimw does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.2024 05/10/13 15:32:31 jimw@stripped +3 -0
  Fix calculation of TIMESTAMPDIFF() of months and years. (Bug #13534)

  sql/item_timefunc.cc
    1.95 05/10/13 15:32:28 jimw@stripped +21 -42
    Fix calculation of years and months in TIMESTAMPDIFF().

  mysql-test/t/func_time.test
    1.38 05/10/13 15:32:28 jimw@stripped +24 -0
    Add new regression test

  mysql-test/r/func_time.result
    1.47 05/10/13 15:32:28 jimw@stripped +51 -0
    Add new results

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	jimw
# Host:	rama.(none)
# Root:	/home/jimw/my/mysql-5.0-13534

--- 1.94/sql/item_timefunc.cc	2005-09-14 09:24:30 -07:00
+++ 1.95/sql/item_timefunc.cc	2005-10-13 15:32:28 -07:00
@@ -2723,16 +2723,16 @@
       int_type == INTERVAL_QUARTER ||
       int_type == INTERVAL_MONTH)
   {
-    uint year;
-    uint year_beg, year_end, month_beg, month_end;
-    uint diff_days= (uint) (seconds/86400L);
-    uint diff_years= 0;
+    uint year_beg, year_end, month_beg, month_end, day_beg, day_end;
+    uint years= 0;
     if (neg == -1)
     {
       year_beg= ltime2.year;
       year_end= ltime1.year;
       month_beg= ltime2.month;
       month_end= ltime1.month;
+      day_beg= ltime2.day;
+      day_end= ltime1.day;
     }
     else
     {
@@ -2740,53 +2740,32 @@
       year_end= ltime2.year;
       month_beg= ltime1.month;
       month_end= ltime2.month;
-    }
-    /* calc years*/
-    for (year= year_beg;year < year_end; year++)
-    {
-      uint days=calc_days_in_year(year);
-      if (days > diff_days)
-	break;
-      diff_days-= days;
-      diff_years++;
+      day_beg= ltime1.day;
+      day_end= ltime2.day;
     }
 
-    /* calc months;  Current year is in the 'year' variable */
-    month_beg--;	/* Change months to be 0-11 for easier calculation */
-    month_end--;
+    /* calc years */
+    years= year_end - year_beg;
+    if (month_end < month_beg || (month_end == month_beg && day_end <
day_beg))
+      years-= 1;
 
-    months= 12*diff_years;
-    while (month_beg != month_end)
-    {
-      uint m_days= (uint) days_in_month[month_beg];
-      if (month_beg == 1)
-      {
-	/* This is only calculated once so there is no reason to cache it*/
-	uint leap= (uint) ((year & 3) == 0 && (year%100 ||
-					       (year%400 == 0 && year)));
-	m_days+= leap;
-      }
-      if (m_days > diff_days)
-	break;
-      diff_days-= m_days;
-      months++;
-      if (month_beg++ == 11)		/* if we wrap to next year */
-      {
-	month_beg= 0;
-	year++;
-      }
-    }
-    if (neg == -1)
-      months= -months;
+    /* calc months */
+    months= 12*years;
+    if (month_end < month_beg || (month_end == month_beg && day_end <
day_beg))
+      months+= 12 - (month_beg - month_end);
+    else
+      months+= (month_end - month_beg);
+    if (day_end < day_beg)
+      months-= 1;
   }
 
   switch (int_type) {
   case INTERVAL_YEAR:
-    return months/12;
+    return months/12*neg;
   case INTERVAL_QUARTER:
-    return months/3;
+    return months/3*neg;
   case INTERVAL_MONTH:
-    return months;
+    return months*neg;
   case INTERVAL_WEEK:          
     return seconds/86400L/7L*neg;
   case INTERVAL_DAY:		

--- 1.46/mysql-test/r/func_time.result	2005-09-14 09:24:30 -07:00
+++ 1.47/mysql-test/r/func_time.result	2005-10-13 15:32:28 -07:00
@@ -760,3 +760,54 @@
 @a != @b
 1
 drop procedure t_sysdate;
+select timestampdiff(month,'2004-09-11','2004-09-11');
+timestampdiff(month,'2004-09-11','2004-09-11')
+0
+select timestampdiff(month,'2004-09-11','2005-09-11');
+timestampdiff(month,'2004-09-11','2005-09-11')
+12
+select timestampdiff(month,'2004-09-11','2006-09-11');
+timestampdiff(month,'2004-09-11','2006-09-11')
+24
+select timestampdiff(month,'2004-09-11','2007-09-11');
+timestampdiff(month,'2004-09-11','2007-09-11')
+36
+select timestampdiff(month,'2005-09-11','2004-09-11');
+timestampdiff(month,'2005-09-11','2004-09-11')
+-12
+select timestampdiff(month,'2005-09-11','2003-09-11');
+timestampdiff(month,'2005-09-11','2003-09-11')
+-24
+select timestampdiff(month,'2004-02-28','2005-02-28');
+timestampdiff(month,'2004-02-28','2005-02-28')
+12
+select timestampdiff(month,'2004-02-29','2005-02-28');
+timestampdiff(month,'2004-02-29','2005-02-28')
+11
+select timestampdiff(month,'2004-02-28','2005-02-28');
+timestampdiff(month,'2004-02-28','2005-02-28')
+12
+select timestampdiff(month,'2004-03-29','2005-03-28');
+timestampdiff(month,'2004-03-29','2005-03-28')
+11
+select timestampdiff(month,'2003-02-28','2004-02-29');
+timestampdiff(month,'2003-02-28','2004-02-29')
+12
+select timestampdiff(month,'2003-02-28','2005-02-28');
+timestampdiff(month,'2003-02-28','2005-02-28')
+24
+select timestampdiff(month,'1999-09-11','2001-10-10');
+timestampdiff(month,'1999-09-11','2001-10-10')
+24
+select timestampdiff(month,'1999-09-11','2001-9-11');
+timestampdiff(month,'1999-09-11','2001-9-11')
+24
+select timestampdiff(year,'1999-09-11','2001-9-11');
+timestampdiff(year,'1999-09-11','2001-9-11')
+2
+select timestampdiff(year,'2004-02-28','2005-02-28');
+timestampdiff(year,'2004-02-28','2005-02-28')
+1
+select timestampdiff(year,'2004-02-29','2005-02-28');
+timestampdiff(year,'2004-02-29','2005-02-28')
+0

--- 1.37/mysql-test/t/func_time.test	2005-08-24 15:50:54 -07:00
+++ 1.38/mysql-test/t/func_time.test	2005-10-13 15:32:28 -07:00
@@ -404,4 +404,28 @@
 call t_sysdate();
 drop procedure t_sysdate;
 
+#
+# Bug #13534: timestampdiff() returned incorrect results across leap years
+#
+select timestampdiff(month,'2004-09-11','2004-09-11');
+select timestampdiff(month,'2004-09-11','2005-09-11');
+select timestampdiff(month,'2004-09-11','2006-09-11');
+select timestampdiff(month,'2004-09-11','2007-09-11');
+select timestampdiff(month,'2005-09-11','2004-09-11');
+select timestampdiff(month,'2005-09-11','2003-09-11');
+
+select timestampdiff(month,'2004-02-28','2005-02-28');
+select timestampdiff(month,'2004-02-29','2005-02-28');
+select timestampdiff(month,'2004-02-28','2005-02-28');
+select timestampdiff(month,'2004-03-29','2005-03-28');
+select timestampdiff(month,'2003-02-28','2004-02-29');
+select timestampdiff(month,'2003-02-28','2005-02-28');
+
+select timestampdiff(month,'1999-09-11','2001-10-10');
+select timestampdiff(month,'1999-09-11','2001-9-11');
+
+select timestampdiff(year,'1999-09-11','2001-9-11');
+select timestampdiff(year,'2004-02-28','2005-02-28');
+select timestampdiff(year,'2004-02-29','2005-02-28');
+
 # End of 5.0 tests
Thread
bk commit into 5.0 tree (jimw:1.2024) BUG#13534Jim Winstead14 Oct