#include "config.h"
#include "slrnfeat.h"
/* Copyright (c) 1998 John E. Davis (davis@space.mit.edu)
 *
 * This file is part of slrn.
 *
 * Slrn is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 * 
 * Slrn is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Slrn; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place - Suite 330, 
 * Boston, MA  02111-1307, USA.
 */


#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "slrn.h"
#include "util.h"

#ifndef isdigit
# define isdigit(x) (((x)>='0')&&((x)<='9'))
#endif
#ifndef isalpha
# define isalpha(x) ((((x)>='A')&&((x)<='Z')) || (((x)>='A')&&((x)<='Z')))
#endif

/* This routine parses the date field of a header and returns it as a time_t
 * object.  The main difficulity is the lack of uniformity in the date formats
 * and the fact that there are leap years, etc...
 * 
 * Grepping my news spool of more than 12000 articles revealed that the 
 * date headers of the vast majority of headers appear in one of the 
 * following forms:
 * 
 *   03 Feb 1997 10:09:58 +0100
 *   5 Feb 1997 15:32:05 GMT
 *   Wed, 05 Feb 1997 11:58:33 -0800
 * 
 * A few look like:
 *   Fri, 3 Jan 1997 07:35:15 -0800 (PST)
 * 
 * and very few (less than 1 percent) of the articles had Date headers of the
 * form:
 *
 *   Tue, 21 Jan 1997 14:17:31 UNDEFINED
 *   02 Feb 97 14:20:35 EDT
 *   Tue, 4 Feb 1997 19:06:46 LOCAL
 *   Tue, 3 Feb 1997 11:51:27 UT
 *
 * This means that the timezone can be assumed to be GMT or specified via an
 * offset from GMT.  All of the above dates have the format:
 *  
 *    [Weekday,] Day Month Year Hour:Min:Sec Timezone
 * 
 * which will make the parsing somewhat simple.
 * 
 * Beware: the calculations involving leap years, etc... are naive.
 */

#define THE_YEAR_0		1992L  /* leap year */
#define IS_LEAP_YEAR(y)		(((y) % 4) == 0)

typedef struct 
{
   char name[5];
   int tz;
}
Timezone_Table_Type;

static Timezone_Table_Type Timezone_Table [] =
{
   {"EDT", -500},
   {"PST", -800},
   {"CST", -600},
   {"MST", -700},
   {"MET", 100},		       /* Middle European */
   {"MEZ", 100},		       /* Middle European */
   {"MSK", 300},		       /* Moscow */
   {"CET", 100},		       /* Central European */
   {"HKT", 800},		       /* Central European */
   {"JST", 900},		       /* Central European */
   {"CAST", 930},		       /* Central Autsralian */
   {"EAST", 1000},		       /* Eastern Autsralian */
   {"NZST", 1200},		       /* New Zealand Autsralian */
   {"EET", 200},		       /* Eastern European */
   {"", 0}
};

   
static int parse_timezone (char *t)
{
   char ch;
   Timezone_Table_Type *table;
   
   table = Timezone_Table;
   
   while (0 != (ch = table->name [0]))
     {
	if ((*t == ch) && (0 == strncmp (t, table->name, sizeof (table->name))))
	  return table->tz;
	
	table++;
     }
   
   return 0;
}

long slrn_date_to_order_parm (char *date)
{
   char ch;
   long day, month, year, minutes, seconds, hours, tz;
   int sign;
   
   date = slrn_skip_whitespace (date);
   if ((date == NULL) || (*date == 0))
     return 0;
   
   /* Look for a weekday, if found skip it */
   while (isalpha (*date))
     date++;
   if (*date == ',') date++;
   
   date = slrn_skip_whitespace (date);
   
   /* expecting "03 Feb 1997 10:09:58 +0100" */
   day = 0;
   while (ch = *date, isdigit (ch))
     {
	day = 10 * day + (long) (ch - '0');
	date++;
     }
   if ((day == 0) || (day > 31))
     return 0;
   day--;
   
   date = slrn_skip_whitespace (date);
   month = 0;
   switch (*date++)
     {
      default: 
	return 0;
      case 'J':			       /* Jan, Jun, Jul */
	ch = *date++;
	if ((ch == 'u') || (ch == 'U'))
	  {
	     ch = *date++;
	     if ((ch == 'n') || (ch == 'N'))
	       month = 6;
	     else if ((ch == 'l') || (ch == 'L'))
	       month = 7;
	  }
	else if ((ch == 'a') || (ch == 'A'))
	  month = 1;
	break;
	
      case 'F': case 'f':	       /* Feb */
	month = 2;
	break;
      case 'M': case 'm':	       /* May, Mar */
	ch = *date++;
	if ((ch == 'a') || (ch == 'A'))
	  {
	     ch = *date++;
	     if ((ch == 'y') || (ch == 'Y')) month = 5;
	     else if ((ch == 'r') || (ch == 'R')) month = 3;
	  }
	break;
      case 'A': case 'a':	       /* Apr, Aug */
	ch = *date++;
	if ((ch == 'p') || (ch == 'P')) month = 4;
	else if ((ch == 'u') || (ch == 'U')) month = 8;
	break;
	
      case 'S': case 's':	       /* Sep */
	month = 9;
	break;
      case 'O': case 'o':	       /* Oct */
	month = 10;
	break;
      case 'N': case 'n':	       /* Nov */
	month = 11;
	break;
      case 'D': case 'd':	       /* Dec */
	month = 12;
	break;
     }
   
   if (month == 0)
     return 0;
   
   month--;
   
   /* skip past month onto year. */
   while (isalpha (*date))
     date++;
   date = slrn_skip_whitespace (date);

   year = 0;
#if 1
   /* Sigh.  Y2K problem handling dates of the form 02 Feb 97 14:20:35 EDT. */
   if (*date == '9')
     year = 19;
#endif

   while (ch = *date, isdigit (ch))
     {
	year = year * 10 + (long) (ch - '0');
	date++;
     }
   date = slrn_skip_whitespace (date);
   
   /* Now parse hh:mm:ss */
   hours = 0;
   while (ch = *date, isdigit (ch))
     {
	hours = hours * 10 + (long) (ch - '0');
	date++;
     }
   if ((ch != ':') || (hours >= 24)) return 0;
   date++;
   
   minutes = 0;
   while (ch = *date, isdigit (ch))
     {
	minutes = minutes * 10 + (long) (ch - '0');
	date++;
     }
   if (minutes >= 60)
     return 0;
   
   /* Seconds may not be present */
   seconds = 0;
   if (ch == ':')
     {
	date++;
	while (ch = *date, isdigit (ch))
	  {
	     seconds = seconds * 10 + (long) (ch - '0');
	     date++;
	  }
	if (seconds >= 60) return 0;
     }
	     
   /* Now timezone */
   date = slrn_skip_whitespace (date);
   
   sign = 1;
   if (*date == '+')
     date++;
   else if (*date == '-')
     {
	sign = -1;
	date++;
     }
   
   tz = 0;
   while (ch = *date, isdigit(ch))
     {
	tz = tz * 10 + (long) (ch - '0');
	date++;
     }
   tz = sign * tz;
   
   date = slrn_skip_whitespace (date);
   if (isalpha (*date))
     {
	sign = 1;
	if ((*date != 'G') && (*date != 'M') && (*date != 'T'))
	  tz = parse_timezone (date);
     }

   /* Compute the number of days since beginning of year. */
   day = 31 * month + day;
   if (month > 1)
     {
	day -= (month * 4 + 27)/10;
	if (IS_LEAP_YEAR(year)) day++;
     }
   
   /* add that to number of days since beginning of time */
   year -= THE_YEAR_0;
   day += year * 365 + year / 4;
   if ((year % 4) == 1) day++;
   
   /* Adjust hours for timezone */
   hours -= tz / 100;
   minutes -= tz % 100;		       /* ?? */
   
   
   /* Now convert to secs */
   seconds += 60L * (minutes + 60L * (hours +  24L * day));
   
   return seconds;
}
