diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result
index e330c73727b603b2104842f1dd769c4c2c7e82a6..6bd4c6f46a251261d7f61f42634c13a8d7c06ede 100644
--- a/mysql-test/r/func_sapdb.result
+++ b/mysql-test/r/func_sapdb.result
@@ -188,13 +188,14 @@ ttt	qqq
 NULL	NULL
 NULL	NULL
 2001-01-01 02:02:02	26:02:02
-SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test;
-ttt	qqq
--744:00:00	NULL
-26305:01:02	22:58:58
--26305:01:02	-22:58:58
-NULL	26:02:02
-NULL	NULL
-NULL	NULL
-00:00:00	-24:00:00
+SELECT TIMEDIFF(t1, t4) As ttt, TIMEDIFF(t2, t3) As qqq,
+TIMEDIFF(t3, t2) As eee, TIMEDIFF(t2, t4) As rrr from test;
+ttt	qqq	eee	rrr
+-744:00:00	NULL	NULL	NULL
+26305:01:02	22:58:58	-22:58:58	NULL
+-26305:01:02	-22:58:58	22:58:58	NULL
+NULL	26:02:02	26:02:02	NULL
+NULL	NULL	NULL	NULL
+NULL	NULL	NULL	NULL
+00:00:00	-24:00:00	24:00:00	NULL
 drop table t1, test;
diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result
index feaebc9b51413cb1a0d4cf52925076d837f138dd..e4804790ed0f23f4c31a2e794fae83d59fa975f7 100644
--- a/mysql-test/r/func_time.result
+++ b/mysql-test/r/func_time.result
@@ -464,6 +464,63 @@ date_add(date,INTERVAL "1 1:1" DAY_MINUTE)
 select date_add(date,INTERVAL "1 1:1:1" DAY_SECOND) from t1;
 date_add(date,INTERVAL "1 1:1:1" DAY_SECOND)
 2003-01-03 01:01:01
+select date_add(date,INTERVAL "1" WEEK) from t1;
+date_add(date,INTERVAL "1" WEEK)
+2003-01-09 00:00:00
+select date_add(date,INTERVAL "1" QUARTER) from t1;
+date_add(date,INTERVAL "1" QUARTER)
+2003-04-02
+select timestampadd(MINUTE, 1, date) from t1;
+timestampadd(MINUTE, 1, date)
+2003-01-02 00:01:00
+select timestampadd(WEEK, 1, date) from t1;
+timestampadd(WEEK, 1, date)
+2003-01-09 00:00:00
+select timestampadd(SQL_TSI_SECOND, 1, date) from t1;
+timestampadd(SQL_TSI_SECOND, 1, date)
+2003-01-02 00:00:01
+select timestampadd(SQL_TSI_FRAC_SECOND, 1, date) from t1;
+timestampadd(SQL_TSI_FRAC_SECOND, 1, date)
+2003-01-02 00:00:00.000001
+select timestampdiff(MONTH, '2001-02-01', '2001-05-01') as a;
+a
+3
+select timestampdiff(YEAR, '2002-05-01', '2001-01-01') as a;
+a
+-1
+select timestampdiff(QUARTER, '2002-05-01', '2001-01-01') as a;
+a
+-5
+select timestampdiff(MONTH, '2000-03-28', '2000-02-29') as a;
+a
+0
+select timestampdiff(MONTH, '1991-03-28', '2000-02-29') as a;
+a
+107
+select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a;
+a
+12
+select timestampdiff(SQL_TSI_HOUR, '2001-02-01', '2001-05-01') as a;
+a
+2136
+select timestampdiff(SQL_TSI_DAY, '2001-02-01', '2001-05-01') as a;
+a
+89
+select timestampdiff(SQL_TSI_MINUTE, '2001-02-01 12:59:59', '2001-05-01 12:58:59') as a;
+a
+128159
+select timestampdiff(SQL_TSI_SECOND, '2001-02-01 12:59:59', '2001-05-01 12:58:58') as a;
+a
+7689539
+select timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a;
+a
+7689538999999
+select timestampdiff(SQL_TSI_DAY, '1986-02-01', '1986-03-01') as a1,
+timestampdiff(SQL_TSI_DAY, '1900-02-01', '1900-03-01') as a2,
+timestampdiff(SQL_TSI_DAY, '1996-02-01', '1996-03-01') as a3,
+timestampdiff(SQL_TSI_DAY, '2000-02-01', '2000-03-01') as a4;
+a1	a2	a3	a4
+28	28	29	29
 select date_add(time,INTERVAL 1 SECOND) from t1;
 date_add(time,INTERVAL 1 SECOND)
 2006-07-08 00:00:01
diff --git a/mysql-test/r/keywords.result b/mysql-test/r/keywords.result
index c218379110fd05eae41c2f0ed3980117c7ce6cd0..88a0ab8abd5cdcc1776e5e5854d6510a15c7458d 100644
--- a/mysql-test/r/keywords.result
+++ b/mysql-test/r/keywords.result
@@ -1,12 +1,14 @@
 drop table if exists t1;
-create table t1 (time time, date date, timestamp timestamp);
-insert into t1 values ("12:22:22","97:02:03","1997-01-02");
+create table t1 (time time, date date, timestamp timestamp,
+quarter int, week int, year int, timestampadd int, timestampdiff int);
+insert into t1 values ("12:22:22","97:02:03","1997-01-02",1,2,3,4,5);
 select * from t1;
-time	date	timestamp
-12:22:22	1997-02-03	1997-01-02 00:00:00
-select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time) from t1;
-t1.time+0	t1.date+0	t1.timestamp+0	concat(date," ",time)
-122222	19970203	19970102000000	1997-02-03 12:22:22
+time	date	timestamp	quarter	week	year	timestampadd	timestampdiff
+12:22:22	1997-02-03	1997-01-02 00:00:00	1	2	3	4	5
+select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time),
+t1.quarter+t1.week, t1.year+timestampadd,  timestampdiff from t1;
+t1.time+0	t1.date+0	t1.timestamp+0	concat(date," ",time)	t1.quarter+t1.week	t1.year+timestampadd	timestampdiff
+122222	19970203	19970102000000	1997-02-03 12:22:22	3	7	5
 drop table t1;
 create table events(binlog int);
 insert into events values(1);
diff --git a/mysql-test/t/func_sapdb.test b/mysql-test/t/func_sapdb.test
index afd84fe9630e968712d9a78288e00bfcaccc0015..ba64e13873d968a7775df4f1013baf81923b9e45 100644
--- a/mysql-test/t/func_sapdb.test
+++ b/mysql-test/t/func_sapdb.test
@@ -94,6 +94,7 @@ insert into test values
 ('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01');
 
 SELECT ADDTIME(t1,t2) As ttt, ADDTIME(t2, t3) As qqq from test;
-SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test;
+SELECT TIMEDIFF(t1, t4) As ttt, TIMEDIFF(t2, t3) As qqq,
+       TIMEDIFF(t3, t2) As eee, TIMEDIFF(t2, t4) As rrr from test;
 
 drop table t1, test;
diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test
index f1a24a4c3cbe12bc7ca7b97b978887b4798769ad..955b0358ae36d89371916ce90c8ddbbab2823ca2 100644
--- a/mysql-test/t/func_time.test
+++ b/mysql-test/t/func_time.test
@@ -220,6 +220,30 @@ select date_add(date,INTERVAL "1:1:1" HOUR_SECOND) from t1;
 select date_add(date,INTERVAL "1 1:1" DAY_MINUTE) from t1;
 select date_add(date,INTERVAL "1 1:1:1" DAY_SECOND) from t1;
 
+select date_add(date,INTERVAL "1" WEEK) from t1;
+select date_add(date,INTERVAL "1" QUARTER) from t1;
+select timestampadd(MINUTE, 1, date) from t1;
+select timestampadd(WEEK, 1, date) from t1;
+select timestampadd(SQL_TSI_SECOND, 1, date) from t1;
+select timestampadd(SQL_TSI_FRAC_SECOND, 1, date) from t1;
+
+select timestampdiff(MONTH, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(YEAR, '2002-05-01', '2001-01-01') as a;
+select timestampdiff(QUARTER, '2002-05-01', '2001-01-01') as a;
+select timestampdiff(MONTH, '2000-03-28', '2000-02-29') as a;
+select timestampdiff(MONTH, '1991-03-28', '2000-02-29') as a;
+select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(SQL_TSI_HOUR, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(SQL_TSI_DAY, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(SQL_TSI_MINUTE, '2001-02-01 12:59:59', '2001-05-01 12:58:59') as a;
+select timestampdiff(SQL_TSI_SECOND, '2001-02-01 12:59:59', '2001-05-01 12:58:58') as a;
+select timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a;
+
+select timestampdiff(SQL_TSI_DAY, '1986-02-01', '1986-03-01') as a1,
+       timestampdiff(SQL_TSI_DAY, '1900-02-01', '1900-03-01') as a2,
+       timestampdiff(SQL_TSI_DAY, '1996-02-01', '1996-03-01') as a3,
+       timestampdiff(SQL_TSI_DAY, '2000-02-01', '2000-03-01') as a4;
+
 # The following is not as one would expect...
 select date_add(time,INTERVAL 1 SECOND) from t1;
 drop table t1;
diff --git a/mysql-test/t/keywords.test b/mysql-test/t/keywords.test
index e7ec63afe5480e51b01044261d9cd580258099e4..3392bfa1b3b514fd8d4385c856dbb57940c7adb4 100644
--- a/mysql-test/t/keywords.test
+++ b/mysql-test/t/keywords.test
@@ -6,10 +6,12 @@
 drop table if exists t1;
 --enable_warnings
 
-create table t1 (time time, date date, timestamp timestamp);
-insert into t1 values ("12:22:22","97:02:03","1997-01-02");
+create table t1 (time time, date date, timestamp timestamp,
+quarter int, week int, year int, timestampadd int, timestampdiff int);
+insert into t1 values ("12:22:22","97:02:03","1997-01-02",1,2,3,4,5);
 select * from t1;
-select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time) from t1;
+select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time),
+       t1.quarter+t1.week, t1.year+timestampadd,  timestampdiff from t1;
 drop table t1;
 create table events(binlog int);
 insert into events values(1);
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 1ddb1fcb080fe4debc47e70d839938d75e73e452..7a548962cde3a4db778de5a9b8c972406a4e729d 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -312,11 +312,6 @@ Item *create_func_current_user()
 			 system_charset_info);
 }
 
-Item *create_func_quarter(Item* a)
-{
-  return new Item_func_quarter(a);
-}
-
 Item *create_func_radians(Item *a)
 {
   return new Item_func_units((char*) "radians",a,M_PI/180,0.0);
diff --git a/sql/item_create.h b/sql/item_create.h
index 083868d87a6bd3db7fbfb7d01237772fd489f052..b445eab3bacdf71878c701cdf4b29856087bddd9 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -72,7 +72,6 @@ Item *create_func_period_diff(Item* a, Item *b);
 Item *create_func_pi(void);
 Item *create_func_pow(Item* a, Item *b);
 Item *create_func_current_user(void);
-Item *create_func_quarter(Item* a);
 Item *create_func_radians(Item *a);
 Item *create_func_release_lock(Item* a);
 Item *create_func_repeat(Item* a, Item *b);
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index a5372a4ae60fe6ac82712986128d266ccaa70184..661903715759ce303fb5ca256313a783dc71352a 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -869,9 +869,15 @@ static bool get_interval_value(Item *args,interval_type int_type,
   case INTERVAL_YEAR:
     interval->year=value;
     break;
+  case INTERVAL_QUARTER:
+    interval->month=value*3;
+    break;
   case INTERVAL_MONTH:
     interval->month=value;
     break;
+  case INTERVAL_WEEK:
+    interval->day=value*7;
+    break;
   case INTERVAL_DAY:
     interval->day=value;
     break;
@@ -1547,6 +1553,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
       goto null_date;
     break;
   case INTERVAL_DAY:
+  case INTERVAL_WEEK:
     period= calc_daynr(ltime->year,ltime->month,ltime->day) +
       sign*interval.day;
     if (period < 0 || period >= MAX_DAY_NUMBER) // Daynumber from year 0 to 9999-12-31
@@ -1562,6 +1569,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
       ltime->day=28;				// Was leap-year
     break;
   case INTERVAL_YEAR_MONTH:
+  case INTERVAL_QUARTER:
   case INTERVAL_MONTH:
     period= (ltime->year*12 + sign*interval.year*12 +
 	     ltime->month-1 + sign*interval.month);
@@ -1659,7 +1667,9 @@ void Item_extract::fix_length_and_dec()
   switch (int_type) {
   case INTERVAL_YEAR:		max_length=4; date_value=1; break;
   case INTERVAL_YEAR_MONTH:	max_length=6; date_value=1; break;
+  case INTERVAL_QUARTER:        max_length=2; date_value=1; break;
   case INTERVAL_MONTH:		max_length=2; date_value=1; break;
+  case INTERVAL_WEEK:		max_length=2; date_value=1; break;
   case INTERVAL_DAY:		max_length=2; date_value=1; break;
   case INTERVAL_DAY_HOUR:	max_length=9; date_value=0; break;
   case INTERVAL_DAY_MINUTE:	max_length=11; date_value=0; break;
@@ -1682,6 +1692,8 @@ void Item_extract::fix_length_and_dec()
 longlong Item_extract::val_int()
 {
   TIME ltime;
+  uint year;
+  ulong week_format;
   long neg;
   if (date_value)
   {
@@ -1703,7 +1715,16 @@ longlong Item_extract::val_int()
   switch (int_type) {
   case INTERVAL_YEAR:		return ltime.year;
   case INTERVAL_YEAR_MONTH:	return ltime.year*100L+ltime.month;
+  case INTERVAL_QUARTER:	return ltime.month/3 + 1;
   case INTERVAL_MONTH:		return ltime.month;
+  case INTERVAL_WEEK:
+  {
+    week_format= current_thd->variables.default_week_format;
+    return calc_week(&ltime, 
+		     (week_format & 2) != 0,
+		     (week_format & 1) == 0,
+		     &year);
+  }
   case INTERVAL_DAY:		return ltime.day;
   case INTERVAL_DAY_HOUR:	return (long) (ltime.day*100L+ltime.hour)*neg;
   case INTERVAL_DAY_MINUTE:	return (long) (ltime.day*10000L+
@@ -2085,6 +2106,79 @@ void Item_func_add_time::print(String *str)
 }
 
 
+/*
+  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.
+
+  RETURN VALUES
+    Rertuns sign of difference.
+    1 means negative result
+    0 means positive result
+
+*/
+
+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;
+
+  /*
+    We suppose that if first argument is TIMESTAMP_TIME
+    the second argument should be TIMESTAMP_TIME also.
+    We should check it before calc_time_diff call.
+  */
+  if (l_time1->time_type == TIMESTAMP_TIME)  // Time value
+    days= l_time1->day - l_sign*l_time2->day;
+  else                                      // DateTime value
+    days= (calc_daynr((uint) l_time1->year,
+		      (uint) l_time1->month,
+		      (uint) l_time1->day) - 
+	   l_sign*calc_daynr((uint) l_time2->year,
+			     (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));
+
+  neg= 0;
+  if (seconds < 0)
+  {
+    seconds= -seconds;
+    neg= 1;
+  }
+  else if (seconds == 0 && microseconds < 0)
+  {
+    microseconds= -microseconds;
+    neg= 1;
+  }
+  if (microseconds < 0)
+  {
+    microseconds+= 1000000L;
+    seconds--;
+  }
+  *seconds_out= seconds;
+  *microseconds_out= microseconds;
+  return neg;
+}
+
 /*
   TIMEDIFF(t,s) is a time function that calculates the 
   time value between a start and end time.
@@ -2110,40 +2204,16 @@ String *Item_func_timediff::val_str(String *str)
   if (l_time1.neg != l_time2.neg)
     l_sign= -l_sign;
 
-  if (l_time1.time_type == TIMESTAMP_TIME)  // Time value
-    days= l_time1.day - l_sign*l_time2.day;
-  else                                      // DateTime value
-    days= (calc_daynr((uint) l_time1.year,
-		      (uint) l_time1.month,
-		      (uint) l_time1.day) - 
-	   l_sign*calc_daynr((uint) l_time2.year,
-			     (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));
+  l_time3.neg= calc_time_diff(&l_time1,&l_time2, l_sign,
+			      &seconds, &microseconds);
 
-  l_time3.neg= 0;
-  if (seconds < 0)
-  {
-    seconds= -seconds;
-    l_time3.neg= 1;
-  }
-  else if (seconds == 0 && microseconds < 0)
-  {
-    microseconds= -microseconds;
-    l_time3.neg= 1;
-  }
-  if (microseconds < 0)
-  {
-    microseconds+= 1000000L;
-    seconds--;
-  }
+  /*
+    For 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.
+  */
   if ((l_time2.neg == l_time1.neg) && l_time1.neg)
-    l_time3.neg= l_time3.neg ? 0 : 1;
+    l_time3.neg= 1-l_time3.neg;         // Swap sign of result
 
   calc_time_from_sec(&l_time3, (long) seconds, microseconds);
 
@@ -2210,6 +2280,166 @@ longlong Item_func_microsecond::val_int()
 }
 
 
+longlong Item_func_timestamp_diff::val_int()
+{
+  TIME ltime1, ltime2;
+  longlong seconds;
+  long microseconds;
+  long months= 0;
+  int neg= 1;
+
+  null_value= 0;  
+  if (args[0]->get_date(&ltime1, 0) ||
+      args[1]->get_date(&ltime2, 0))
+    goto null_date;
+
+  if (calc_time_diff(&ltime2,&ltime1, 1,
+		     &seconds, &microseconds))
+    neg= -1;
+
+  if (int_type == INTERVAL_YEAR ||
+      int_type == INTERVAL_QUARTER ||
+      int_type == INTERVAL_MONTH)
+  {
+    uint year, year_tmp;
+    uint year_beg, year_end, month_beg, month_end;
+    uint diff_days= seconds/86400L;
+    uint diff_months= 0;
+    uint diff_years= 0;
+    if (neg == -1)
+    {
+      year_beg= ltime2.year;
+      year_end= ltime1.year;
+      month_beg= ltime2.month;
+      month_end= ltime1.month;
+    }
+    else
+    {
+      year_beg= ltime1.year;
+      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++;
+    }
+
+    /* calc months;  Current year is in the 'year' variable */
+    month_beg--;	/* Change months to be 0-11 for easier calculation */
+    month_end--;
+
+    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;
+  }
+
+  switch (int_type) {
+  case INTERVAL_YEAR:
+    return months/12;
+  case INTERVAL_QUARTER:
+    return months/3;
+  case INTERVAL_MONTH:
+    return months;
+  case INTERVAL_WEEK:          
+    return seconds/86400L/7L*neg;
+  case INTERVAL_DAY:		
+    return seconds/86400L*neg;
+  case INTERVAL_HOUR:		
+    return seconds/3600L*neg;
+  case INTERVAL_MINUTE:		
+    return seconds/60L*neg;
+  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;
+  }
+  default:
+    break;
+  }
+
+null_date:
+  null_value=1;
+  return 0;
+}
+
+
+void Item_func_timestamp_diff::print(String *str)
+{
+  str->append(func_name());
+  str->append('(');
+
+  switch (int_type) {
+  case INTERVAL_YEAR:
+    str->append("YEAR");
+    break;
+  case INTERVAL_QUARTER:
+    str->append("QUARTER");
+    break;
+  case INTERVAL_MONTH:
+    str->append("MONTH");
+    break;
+  case INTERVAL_WEEK:          
+    str->append("WEEK");
+    break;
+  case INTERVAL_DAY:		
+    str->append("DAY");
+    break;
+  case INTERVAL_HOUR:
+    str->append("HOUR");
+    break;
+  case INTERVAL_MINUTE:		
+    str->append("MINUTE");
+    break;
+  case INTERVAL_SECOND:
+    str->append("SECOND");
+    break;		
+  case INTERVAL_MICROSECOND:
+    str->append("SECOND_FRAC");
+    break;
+  default:
+    break;
+  }
+
+  for (uint i=0 ; i < 2 ; i++)
+  {
+    str->append(',');
+    args[i]->print(str);
+  }
+  str->append(')');
+}
+
+
 String *Item_func_get_format::val_str(String *str)
 {
   const char *format_name;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index a81b9f28d922b61d313fc9ee0e48b1907a92f64d..9f1d7dbd85acb5d9176e63e02748d8097dcbc44a 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -543,11 +543,11 @@ class Item_func_sec_to_time :public Item_str_func
 
 enum interval_type
 {
-  INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE,
-  INTERVAL_SECOND, INTERVAL_MICROSECOND ,INTERVAL_YEAR_MONTH,
-  INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND,
-  INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND,
-  INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
+  INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR,
+  INTERVAL_MINUTE, INTERVAL_WEEK, INTERVAL_SECOND, INTERVAL_MICROSECOND ,
+  INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
+  INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
+  INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
   INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND
 };
 
@@ -769,6 +769,23 @@ class Item_func_microsecond :public Item_int_func
 };
 
 
+class Item_func_timestamp_diff :public Item_int_func
+{
+  const interval_type int_type;
+public:
+  Item_func_timestamp_diff(Item *a,Item *b,interval_type type_arg)
+    :Item_int_func(a,b), int_type(type_arg) {}
+  const char *func_name() const { return "timestamp_diff"; }
+  longlong val_int();
+  void fix_length_and_dec()
+  {
+    decimals=0;
+    maybe_null=1;
+  }
+  void print(String *str);
+};
+
+
 enum date_time_format
 {
   USA_FORMAT, JIS_FORMAT, ISO_FORMAT, EUR_FORMAT, INTERNAL_FORMAT
diff --git a/sql/lex.h b/sql/lex.h
index a3cf1ab79befcb28d6f0dd202d3012594fc76b30..9334b7e22cf2fe44ba916a96360e20fe2726f06e 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -179,6 +179,7 @@ static SYMBOL symbols[] = {
   { "FOREIGN",		SYM(FOREIGN),0,0},
   { "FORCE",		SYM(FORCE_SYM),0,0},
   { "FOUND",		SYM(FOUND_SYM),0,0},
+  { "FRAC_SECOND",      SYM(FRAC_SECOND_SYM),0,0},
   { "RAID_TYPE",	SYM(RAID_TYPE),0,0},
   { "RAID_CHUNKS",	SYM(RAID_CHUNKS),0,0},
   { "RAID_CHUNKSIZE",	SYM(RAID_CHUNKSIZE),0,0},
@@ -336,6 +337,7 @@ static SYMBOL symbols[] = {
   { "PROCESS"	,	SYM(PROCESS),0,0},
   { "PROCESSLIST",	SYM(PROCESSLIST_SYM),0,0},
   { "PRIVILEGES",	SYM(PRIVILEGES),0,0},
+  { "QUARTER",		SYM(QUARTER_SYM),0,0},
   { "QUERY",		SYM(QUERY_SYM),0,0},
   { "QUICK",	        SYM(QUICK),0,0},
   { "RAID0",		SYM(RAID_0_SYM),0,0},
@@ -401,6 +403,15 @@ static SYMBOL symbols[] = {
   { "SQL_NO_CACHE",	SYM(SQL_NO_CACHE_SYM), 0, 0},
   { "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT),0,0},
   { "SQL_THREAD",	SYM(SQL_THREAD),0,0},
+  { "SQL_TSI_FRAC_SECOND", SYM(FRAC_SECOND_SYM),0,0},
+  { "SQL_TSI_SECOND",   SYM(SECOND_SYM),0,0},
+  { "SQL_TSI_MINUTE",   SYM(MINUTE_SYM),0,0},
+  { "SQL_TSI_HOUR",     SYM(HOUR_SYM),0,0},
+  { "SQL_TSI_DAY",      SYM(DAY_SYM),0,0},
+  { "SQL_TSI_WEEK",     SYM(WEEK_SYM),0,0},
+  { "SQL_TSI_MONTH",    SYM(MONTH_SYM),0,0},
+  { "SQL_TSI_QUARTER",  SYM(QUARTER_SYM),0,0},
+  { "SQL_TSI_YEAR",     SYM(YEAR_SYM),0,0},
   { "SOUNDS",		SYM(SOUNDS_SYM),0,0},
   { "SSL",		SYM(SSL_SYM),0,0},
   { "STRAIGHT_JOIN",	SYM(STRAIGHT_JOIN),0,0},
@@ -421,6 +432,8 @@ static SYMBOL symbols[] = {
   { "THEN",		SYM(THEN_SYM),0,0},
   { "TIME",		SYM(TIME_SYM),0,0},
   { "TIMESTAMP",	SYM(TIMESTAMP),0,0},
+  { "TIMESTAMPADD",     SYM(TIMESTAMP_ADD),0,0},
+  { "TIMESTAMPDIFF",    SYM(TIMESTAMP_DIFF),0,0},
   { "TINYBLOB",		SYM(TINYBLOB),0,0},
   { "TINYTEXT",		SYM(TINYTEXT),0,0},
   { "TINYINT",		SYM(TINYINT),0,0},
@@ -456,6 +469,7 @@ static SYMBOL symbols[] = {
   { "VARYING",		SYM(VARYING),0,0},
   { "VARBINARY",	SYM(VARBINARY),0,0},
   { "WARNINGS",		SYM(WARNINGS),0,0},
+  { "WEEK",		SYM(WEEK_SYM),0,0},
   { "WITH",		SYM(WITH),0,0},
   { "WORK",		SYM(WORK_SYM),0,0},
   { "WRITE",		SYM(WRITE_SYM),0,0},
@@ -644,7 +658,6 @@ static SYMBOL sql_functions[] = {
   { "POSITION",		SYM(POSITION_SYM),0,0},
   { "POW",		SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
   { "POWER",		SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
-  { "QUARTER",		SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quarter)},
   { "QUOTE",		SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quote)},
   { "RADIANS",		SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)},
   { "RAND",		SYM(RAND),0,0},
@@ -691,7 +704,6 @@ static SYMBOL sql_functions[] = {
   { "UPPER",		SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)},
   { "VARIANCE",		SYM(VARIANCE_SYM),0,0},
   { "VERSION",		SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)},
-  { "WEEK",		SYM(WEEK_SYM),0,0},
   { "WEEKDAY",		SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)},
   { "WEEKOFYEAR",	SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekofyear)},
   { "WITHIN",		SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_within)},
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 5796105200a8ce96217b87f2a08e162d6cf18ed4..a9035b18c23adf978ad1640f67ae0e426fae8d2c 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -81,7 +81,7 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B)
   enum Item_udftype udf_type;
   CHARSET_INFO *charset;
   thr_lock_type lock_type;
-  interval_type interval;
+  interval_type interval, interval_time_st;
   timestamp_type date_time_type;
   st_select_lex *select_lex;
   chooser_compare_func_creator boolfunc2creator;
@@ -459,6 +459,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %token	STRING_SYM
 %token	TEXT_SYM
 %token	TIMESTAMP
+%token	TIMESTAMP_ADD
+%token	TIMESTAMP_DIFF
 %token	TIME_SYM
 %token	TINYBLOB
 %token	TINYINT
@@ -502,6 +504,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %token	FIELD_FUNC
 %token	FORMAT_SYM
 %token	FOR_SYM
+%token  FRAC_SECOND_SYM
 %token	FROM_UNIXTIME
 %token	GEOMCOLLFROMTEXT
 %token	GEOMFROMTEXT
@@ -547,6 +550,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %token  POLYGON
 %token	POSITION_SYM
 %token	PROCEDURE
+%token	QUARTER_SYM
 %token	RAND
 %token	REPLACE
 %token	RIGHT
@@ -691,6 +695,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %type <date_time_type> date_time_type;
 %type <interval> interval
 
+%type <interval_time_st> interval_time_st
+
 %type <db_type> table_types
 
 %type <row_type> row_types
@@ -3766,6 +3772,8 @@ simple_expr:
 			Geometry::wkbPolygon, Geometry::wkbLineString); }
 	| POSITION_SYM '(' no_in_expr IN_SYM expr ')'
 	  { $$ = new Item_func_locate($5,$3); }
+	| QUARTER_SYM '(' expr ')'
+	  { $$ = new Item_func_quarter($3); }
 	| RAND '(' expr ')'
 	  { $$= new Item_func_rand($3); Lex->uncacheable(UNCACHEABLE_RAND);}
 	| RAND '(' ')'
@@ -3799,6 +3807,10 @@ simple_expr:
 	  { $$= new Item_datetime_typecast($3); }
 	| TIMESTAMP '(' expr ',' expr ')'
 	  { $$= new Item_func_add_time($3, $5, 1, 0); }
+	| TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')'
+	  { $$= new Item_date_add_interval($7,$5,$3,0); }
+	| TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')'
+	  { $$= new Item_func_timestamp_diff($5,$7,$3); }
 	| TRIM '(' expr ')'
 	  { $$= new Item_func_trim($3); }
 	| TRIM '(' LEADING expr FROM expr ')'
@@ -4289,23 +4301,29 @@ using_list:
 	  };
 
 interval:
-	 DAY_HOUR_SYM		{ $$=INTERVAL_DAY_HOUR; }
+	interval_time_st	{}
+	| DAY_HOUR_SYM		{ $$=INTERVAL_DAY_HOUR; }
 	| DAY_MICROSECOND_SYM	{ $$=INTERVAL_DAY_MICROSECOND; }
 	| DAY_MINUTE_SYM	{ $$=INTERVAL_DAY_MINUTE; }
 	| DAY_SECOND_SYM	{ $$=INTERVAL_DAY_SECOND; }
-	| DAY_SYM		{ $$=INTERVAL_DAY; }
 	| HOUR_MICROSECOND_SYM	{ $$=INTERVAL_HOUR_MICROSECOND; }
 	| HOUR_MINUTE_SYM	{ $$=INTERVAL_HOUR_MINUTE; }
 	| HOUR_SECOND_SYM	{ $$=INTERVAL_HOUR_SECOND; }
-	| HOUR_SYM		{ $$=INTERVAL_HOUR; }
 	| MICROSECOND_SYM	{ $$=INTERVAL_MICROSECOND; }
 	| MINUTE_MICROSECOND_SYM	{ $$=INTERVAL_MINUTE_MICROSECOND; }
 	| MINUTE_SECOND_SYM	{ $$=INTERVAL_MINUTE_SECOND; }
+	| SECOND_MICROSECOND_SYM	{ $$=INTERVAL_SECOND_MICROSECOND; }
+	| YEAR_MONTH_SYM	{ $$=INTERVAL_YEAR_MONTH; };
+
+interval_time_st:
+	DAY_SYM			{ $$=INTERVAL_DAY; }
+	| WEEK_SYM		{ $$=INTERVAL_WEEK; }
+	| HOUR_SYM		{ $$=INTERVAL_HOUR; }
+	| FRAC_SECOND_SYM	{ $$=INTERVAL_MICROSECOND; }
 	| MINUTE_SYM		{ $$=INTERVAL_MINUTE; }
 	| MONTH_SYM		{ $$=INTERVAL_MONTH; }
-	| SECOND_MICROSECOND_SYM	{ $$=INTERVAL_SECOND_MICROSECOND; }
+	| QUARTER_SYM		{ $$=INTERVAL_QUARTER; }
 	| SECOND_SYM		{ $$=INTERVAL_SECOND; }
-	| YEAR_MONTH_SYM	{ $$=INTERVAL_YEAR_MONTH; }
 	| YEAR_SYM		{ $$=INTERVAL_YEAR; };
 
 date_time_type:
@@ -5812,6 +5830,7 @@ keyword:
 	| PREV_SYM		{}
 	| PROCESS		{}
 	| PROCESSLIST_SYM	{}
+	| QUARTER_SYM		{}
 	| QUERY_SYM		{}
 	| QUICK			{}
 	| RAID_0_SYM		{}
@@ -5863,6 +5882,8 @@ keyword:
 	| TRANSACTION_SYM	{}
 	| TRUNCATE_SYM		{}
 	| TIMESTAMP		{}
+	| TIMESTAMP_ADD		{}
+	| TIMESTAMP_DIFF	{}
 	| TIME_SYM		{}
 	| TYPE_SYM		{}
 	| FUNCTION_SYM		{}
@@ -5874,6 +5895,7 @@ keyword:
 	| VARIABLES		{}
 	| VALUE_SYM		{}
 	| WARNINGS		{}
+	| WEEK_SYM		{}
 	| WORK_SYM		{}
 	| X509_SYM		{}
 	| YEAR_SYM		{}