#
# This patch enables date offsets relative to an absolute date in Mutt v1.2.5 
# limit filter and other date pattern inputs. It also fixes a slight and almost
# unimportant glitch in the start date / end date settings of original relative
# dates.
#
# Original Mutt allows:
#
# DD[/MM[/YY]]               day of month of current date or exact date
# DD[/MM[/YY]]-DD[/MM[/YY]]  from date to date
# -DD[/MM[/YY]]              older than or equal to date
# DD[/MM[/YY]]-              from date until now
# <nd, <nw, <nm, <ny         less than n days/weeks/months/years ago
# >nd, >nw, >nm, >ny         more than n days/weeks/months/years ago
# =nd, =nw, =nm, =ny         exactly n days/weeks/months/years ago
#
#
# This patch extends it to the following (all nd days expressions may be
# nw/nm/ny for weeks/months/years as well of course), -nd adjusts the
# minimum date, +nd adjusts the maximum date, and *nd adjusts both the
# minimum date and the maximum date.
#
# DD[/MM[/YY]]-nd            day of month of current date or exact date 
#                            minus n days
# DD[/MM[/YY]]+nd            day of month of current date or exact date 
#                            plus n days 
# DD[/MM[/YY]]*nd            day of month of current date or exact date 
#                            plus/minus n days
#
# More than one relative date expressions may be specified, all having a
# cumulative effect, for example:
#
# DATE-4m-2w+1w*3d           from DATE minus 4 months and 2 weeks and 3
#                            days until DATE plus 1 week and 3 days
#
# A special rule takes place if no absolute date is specified. The
# default minimum and maximum date are set to the current date. The very
# first -nd not preceded by any other expression adjusts both the
# minimum and maximum date according to the given value, all other
# relative dates operate as above.
#
# -3d+1d                     two or three days ago
# -2d-1d                     the same, two or three days ago
# +1d-1d                     from yesterday until tomorrow
# -1w*1d                     one day before or after one week ago
#
#
# Contributor: Eike Rathke <er@erack.de>
# Coded following an idea of Sven Guckes, additional enhancement
# suggestions by Thomas Roessler and Stasinos Konstantopoulos.
#
--- mutt-1.2.5/pattern.c	Sun Apr  9 14:27:37 2000
+++ mutt-1.2.5.date-patch/pattern.c	Mon Feb  5 23:46:44 2001
@@ -99,6 +99,17 @@
 
 #define M_MAXRANGE -1
 
+/* constants for parse_date_range() */
+#define M_PDR_NONE	0x0000
+#define M_PDR_MINUS	0x0001
+#define M_PDR_PLUS	0x0002
+#define M_PDR_WINDOW	0x0004
+#define M_PDR_ABSOLUTE	0x0008
+#define M_PDR_DONE	0x0010
+#define M_PDR_ERROR	0x0100
+#define M_PDR_ERRORDONE	(M_PDR_ERROR | M_PDR_DONE)
+
+
 int mutt_getvaluebychar (char ch, struct mapping_t *table)
 {
   int i;
@@ -368,30 +379,142 @@
    Nm	months
    Nw	weeks
    Nd	days */
-static const char *get_offset (struct tm *tm, const char *s)
+static const char *get_offset (struct tm *tm, const char *s, int sign)
 {
   char *ps;
   int offset = strtol (s, &ps, 0);
+  if ((sign < 0 && offset > 0) || (sign > 0 && offset < 0))
+    offset = -offset;
 
   switch (*ps)
   {
     case 'y':
-      tm->tm_year -= offset;
+      tm->tm_year += offset;
       break;
     case 'm':
-      tm->tm_mon -= offset;
+      tm->tm_mon += offset;
       break;
     case 'w':
-      tm->tm_mday -= 7 * offset;
+      tm->tm_mday += 7 * offset;
       break;
     case 'd':
-      tm->tm_mday -= offset;
+      tm->tm_mday += offset;
       break;
+    default:
+      return s;
   }
   mutt_normalize_time (tm);
   return (ps + 1);
 }
 
+static void adjust_date_range (struct tm *min, struct tm *max)
+{
+  if (min->tm_year > max->tm_year
+      || (min->tm_year == max->tm_year && min->tm_mon > max->tm_mon)
+      || (min->tm_year == max->tm_year && min->tm_mon == max->tm_mon
+	&& min->tm_mday > max->tm_mday))
+  {
+    int tmp;
+    
+    tmp = min->tm_year;
+    min->tm_year = max->tm_year;
+    max->tm_year = tmp;
+      
+    tmp = min->tm_mon;
+    min->tm_mon = max->tm_mon;
+    max->tm_mon = tmp;
+      
+    tmp = min->tm_mday;
+    min->tm_mday = max->tm_mday;
+    max->tm_mday = tmp;
+    
+    min->tm_hour = min->tm_min = min->tm_sec = 0;
+    max->tm_hour = 23;
+    max->tm_min = max->tm_sec = 59;
+  }
+}
+
+static const char * parse_date_range (const char* pc, struct tm *min,
+    struct tm *max, int haveMin, struct tm *baseMin, BUFFER *err)
+{
+  int flag = M_PDR_NONE;	
+  while (*pc && ((flag & M_PDR_DONE) == 0))
+  {
+    const char *pt;
+    char ch = *pc++;
+    SKIPWS (pc);
+    switch (ch)
+    {
+      case '-':
+      {
+	/* try a range of absolute date minus offset of Ndwmy */
+	pt = get_offset (min, pc, -1);
+	if (pc == pt)
+	{
+	  if (flag == M_PDR_NONE)
+	  { /* nothing yet and no offset parsed => absolute date? */
+	    if (!getDate (pc, max, err))
+	      flag |= (M_PDR_ABSOLUTE | M_PDR_ERRORDONE);  /* done bad */
+	    else
+	    {
+	      /* reestablish initial base minimum if not specified */
+	      if (!haveMin)
+		memcpy (min, baseMin, sizeof(struct tm));
+	      flag |= (M_PDR_ABSOLUTE | M_PDR_DONE);  /* done good */
+	    }
+	  }
+	  else
+	    flag |= M_PDR_ERRORDONE;
+	}
+	else
+	{
+	  pc = pt;
+	  if (flag == M_PDR_NONE && !haveMin)
+	  { /* the very first "-3d" without a previous absolute date */
+	    max->tm_year = min->tm_year;
+	    max->tm_mon = min->tm_mon;
+	    max->tm_mday = min->tm_mday;
+	  }
+	  flag |= M_PDR_MINUS;
+	}
+      }
+      break;
+      case '+':
+      { /* enlarge plusRange */
+	pt = get_offset (max, pc, 1);
+	if (pc == pt)
+	  flag |= M_PDR_ERRORDONE;
+	else
+	{
+	  pc = pt;
+	  flag |= M_PDR_PLUS;
+	}
+      }
+      break;
+      case '*':
+      { /* enlarge window in both directions */
+	pt = get_offset (min, pc, -1);
+	if (pc == pt)
+	  flag |= M_PDR_ERRORDONE;
+	else
+	{
+	  pc = get_offset (max, pc, 1);
+	  flag |= M_PDR_WINDOW;
+	}
+      }
+      break;
+      default:
+	flag |= M_PDR_ERRORDONE;
+    }
+    SKIPWS (pc);
+  }
+  if ((flag & M_PDR_ERROR) && !(flag & M_PDR_ABSOLUTE))
+  { /* getDate has its own error message, don't overwrite it here */
+    snprintf (err->data, err->dsize, _("Invalid relative date: %s"), pc-1);
+  }
+  return ((flag & M_PDR_ERROR) ? NULL : pc);
+}
+
 static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
 {
   BUFFER buffer;
@@ -448,9 +571,10 @@
 	exact++;
     }
     tm->tm_hour = 23;
-    tm->tm_min = max.tm_sec = 59;
+    tm->tm_min = tm->tm_sec = 59;
 
-    get_offset (tm, buffer.data + 1);
+    /* force negative offset */
+    get_offset (tm, buffer.data + 1, -1);
 
     if (exact)
     {
@@ -463,7 +587,9 @@
   {
     const char *pc = buffer.data;
 
-    if (*pc != '-')
+    int haveMin = FALSE;
+    int untilNow = FALSE;
+    if (isdigit ((unsigned char)*pc))
     {
       /* mininum date specified */
       if ((pc = getDate (pc, &min, err)) == NULL)
@@ -471,28 +597,46 @@
 	FREE (&buffer.data);
 	return (-1);
       }
-    }
-
-    if (*pc && *pc == '-')
-    {
-      /* max date */
-      pc++; /* skip the `-' */
+      haveMin = TRUE;
       SKIPWS (pc);
-      if (*pc)
-	if (!getDate (pc, &max, err))
-	{
-	  FREE (&buffer.data);
-	  return (-1);
-	}
+      if (*pc == '-')
+      {
+        const char *pt = pc + 1;
+	SKIPWS (pt);
+	untilNow = (*pt == '\0');
+      }
     }
-    else
-    {
-      /* search for messages on a specific day */
+
+    if (!untilNow)
+    { /* max date or relative range/window */
+
+      struct tm baseMin;
+
+      if (!haveMin)
+      { /* save base minimum and set current date, e.g. for "-3d+1d" */
+	time_t now = time (NULL);
+	struct tm *tm = localtime (&now);
+	memcpy (&baseMin, &min, sizeof(baseMin));
+	memcpy (&min, tm, sizeof (min));
+	min.tm_hour = min.tm_sec = min.tm_min = 0;
+      }
+      
+      /* preset max date for relative offsets,
+	 if nothing follows we search for messages on a specific day */
       max.tm_year = min.tm_year;
       max.tm_mon = min.tm_mon;
       max.tm_mday = min.tm_mday;
+
+      if (!parse_date_range (pc, &min, &max, haveMin, &baseMin, err))
+      { /* bail out on any parsing error */
+	FREE (&buffer.data);
+	return (-1);
+      }
     }
   }
+
+  /* Since we allow two dates to be specified we'll have to adjust that. */
+  adjust_date_range (&min, &max);
 
   pat->min = mutt_mktime (&min, 1);
   pat->max = mutt_mktime (&max, 1);
--- mutt-1.2.5/date.c	Fri Mar  3 11:10:07 2000
+++ mutt-1.2.5.date-patch/date.c	Thu Feb  1 21:47:44 2001
@@ -101,33 +101,66 @@
   return (g);
 }
 
+/* Return 1 if month is February of leap year, else 0 */
+static int isLeapYearFeb (struct tm *tm)
+{
+  if (tm->tm_mon == 1)
+  {
+    int y = tm->tm_year + 1900;
+    return (((y & 3) == 0) && (((y % 100) != 0) || ((y % 400) == 0)));
+  }
+  return (0);
+}
+
 void mutt_normalize_time (struct tm *tm)
 {
   static char DaysPerMonth[12] = {
     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
   };
+  int nLeap;
 
   while (tm->tm_sec < 0)
   {
     tm->tm_sec += 60;
     tm->tm_min--;
   }
+  while (tm->tm_sec >= 60)
+  {
+    tm->tm_sec -= 60;
+    tm->tm_min++;
+  }
   while (tm->tm_min < 0)
   {
     tm->tm_min += 60;
     tm->tm_hour--;
   }
+  while (tm->tm_min >= 60)
+  {
+    tm->tm_min -= 60;
+    tm->tm_hour++;
+  }
   while (tm->tm_hour < 0)
   {
     tm->tm_hour += 24;
     tm->tm_mday--;
   }
+  while (tm->tm_hour >= 24)
+  {
+    tm->tm_hour -= 24;
+    tm->tm_mday++;
+  }
+  /* use loops on NNNdwmy user input values? */
   while (tm->tm_mon < 0)
   {
     tm->tm_mon += 12;
     tm->tm_year--;
   }
-  while (tm->tm_mday < 0)
+  while (tm->tm_mon >= 12)
+  {
+    tm->tm_mon -= 12;
+    tm->tm_year++;
+  }
+  while (tm->tm_mday <= 0)
   {
     if (tm->tm_mon)
       tm->tm_mon--;
@@ -136,6 +169,18 @@
       tm->tm_mon = 11;
       tm->tm_year--;
     }
-    tm->tm_mday += DaysPerMonth[tm->tm_mon];
+    tm->tm_mday += DaysPerMonth[tm->tm_mon] + isLeapYearFeb (tm);
+  }
+  while (tm->tm_mday > (DaysPerMonth[tm->tm_mon] + 
+	(nLeap = isLeapYearFeb (tm))))
+  {
+    tm->tm_mday -= DaysPerMonth[tm->tm_mon] + nLeap;
+    if (tm->tm_mon < 11)
+      tm->tm_mon++;
+    else
+    {
+      tm->tm_mon = 0;
+      tm->tm_year++;
+    }
   }
 }
