Commit 9e60778d authored by unknown's avatar unknown
Browse files

Porting fix for bug #7586 "TIMEDIFF for sec+microsec not working properly"

to 5.0 tree (since it was lost during last merge).


mysql-test/r/func_sapdb.result:
  Updated test result after fixing bug #7586 "TIMEDIFF for sec+microsec not
  working properly" in 5.0 tree.
sql/item_timefunc.cc:
  calc_time_diff():
    Use simplier and less error-prone implementation. Now we are counting
    difference between time values in microseconds and then convert it to
    seconds + microseconds value (instead of using seconds and taking
    difference in "second_part"s into account).
parent 6ead8600
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -107,7 +107,7 @@ timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002")
46:58:57.999999
select timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002");
timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002")
-23:59:59.999999
-24:00:00.000001
select timediff("1997-12-31 23:59:59.000001","23:59:59.000001");
timediff("1997-12-31 23:59:59.000001","23:59:59.000001")
NULL
@@ -116,7 +116,7 @@ timediff("2000:01:01 00:00:00", "2000:01:01 00:00:00.000001")
-00:00:00.000001
select timediff("2005-01-11 15:48:49.999999", "2005-01-11 15:48:50");
timediff("2005-01-11 15:48:49.999999", "2005-01-11 15:48:50")
-00:00:01.999999
-00:00:00.000001
select maketime(10,11,12);
maketime(10,11,12)
10:11:12
@@ -188,7 +188,7 @@ f8 date YES NULL
f9	time	YES		NULL	
select * from t1;
f1	f2	f3	f4	f5	f6	f7	f8	f9
1997-01-01	1998-01-02 01:01:00	49:01:01	46:58:57	-23:59:59	10:11:12	2001-12-01 01:01:01	1997-12-31	23:59:59
1997-01-01	1998-01-02 01:01:00	49:01:01	46:58:57	-24:00:00	10:11:12	2001-12-01 01:01:01	1997-12-31	23:59:59
create table test(t1 datetime, t2 time, t3 time, t4 datetime);
insert into test values 
('2001-01-01 01:01:01', '01:01:01', null, '2001-02-01 01:01:01'),
+35 −44
Original line number Diff line number Diff line
@@ -2441,36 +2441,37 @@ void Item_func_add_time::print(String *str)


/*
  Calculate difference between two datetime values as seconds + microseconds.

  SYNOPSIS
    calc_time_diff()
    l_time1		TIME/DATE/DATETIME value
    l_time2		TIME/DATE/DATETIME value
    l_sign		Can be 1 (operation of addition)
                        or -1 (substraction)
    seconds_out         Returns count of seconds bitween
                        l_time1 and l_time2
    microseconds_out    Returns count of microseconds bitween
                        l_time1 and l_time2.

  DESCRIPTION
    Calculates difference in seconds(seconds_out)
    and microseconds(microseconds_out)
    bitween two TIME/DATE/DATETIME values.
      l_time1         - TIME/DATE/DATETIME value
      l_time2         - TIME/DATE/DATETIME value
      l_sign          - 1 absolute values are substracted,
                        -1 absolute values are added.
      seconds_out     - Out parameter where difference between
                        l_time1 and l_time2 in seconds is stored.
      microseconds_out- Out parameter where microsecond part of difference
                        between l_time1 and l_time2 is stored.

  NOTE
    This function calculates difference between l_time1 and l_time2 absolute
    values. So one should set l_sign and correct result if he want to take
    signs into account (i.e. for TIME values).

  RETURN VALUES
    Rertuns sign of difference.
    Returns sign of difference.
    1 means negative result
    0 means positive result

*/

bool calc_time_diff(TIME *l_time1,TIME *l_time2, int l_sign,
static bool calc_time_diff(TIME *l_time1, TIME *l_time2, int l_sign,
                           longlong *seconds_out, long *microseconds_out)
{
  long days;
  bool neg;
  longlong seconds= *seconds_out;
  long microseconds= *microseconds_out;
  longlong microseconds;

  /*
    We suppose that if first argument is MYSQL_TIMESTAMP_TIME
@@ -2487,29 +2488,20 @@ bool calc_time_diff(TIME *l_time1,TIME *l_time2, int l_sign,
			     (uint) l_time2->month,
			     (uint) l_time2->day));

  microseconds= l_time1->second_part - l_sign*l_time2->second_part;
  seconds= ((longlong) days*86400L + l_time1->hour*3600L + 
	    l_time1->minute*60L + l_time1->second + microseconds/1000000L -
	    (longlong)l_sign*(l_time2->hour*3600L+l_time2->minute*60L+l_time2->second));
  microseconds= ((longlong)days*86400L +
                 l_time1->hour*3600L + l_time1->minute*60L + l_time1->second -
                 (longlong)l_sign*(l_time2->hour*3600L + l_time2->minute*60L +
                                   l_time2->second))*1000000L +
                l_time1->second_part - l_sign*l_time2->second_part;

  neg= 0;
  if (seconds < 0)
  {
    seconds= -seconds;
    neg= 1;
  }
  else if (seconds == 0 && microseconds < 0)
  if (microseconds < 0)
  {
    microseconds= -microseconds;
    neg= 1;
  }
  if (microseconds < 0)
  {
    microseconds+= 1000000L;
    seconds--;
  }
  *seconds_out= seconds;
  *microseconds_out= microseconds;
  *seconds_out= microseconds/1000000L;
  *microseconds_out= (long) (microseconds%1000000L);
  return neg;
}

@@ -2544,9 +2536,10 @@ String *Item_func_timediff::val_str(String *str)
  /*
    For MYSQL_TIMESTAMP_TIME only:
      If both argumets are negative values and diff between them
      is negative we need to swap sign as result should be positive.
      is non-zero we need to swap sign to get proper result.
  */
  if ((l_time2.neg == l_time1.neg) && l_time1.neg)
  if ((l_time2.neg == l_time1.neg) && l_time1.neg &&
      (seconds || microseconds))
    l_time3.neg= 1-l_time3.neg;         // Swap sign of result

  calc_time_from_sec(&l_time3, (long) seconds, microseconds);
@@ -2712,13 +2705,11 @@ longlong Item_func_timestamp_diff::val_int()
  case INTERVAL_SECOND:		
    return seconds*neg;
  case INTERVAL_MICROSECOND:
  {
    longlong max_sec= LONGLONG_MAX/1000000;
    if (max_sec > seconds ||
	max_sec == seconds && LONGLONG_MAX%1000000 >= microseconds)
      return (longlong) (seconds*1000000L+microseconds)*neg;
    goto null_date;
  }
    /*
      In MySQL difference between any two valid datetime values
      in microseconds fits into longlong.
    */
    return (seconds*1000000L+microseconds)*neg;
  default:
    break;
  }