//
// HtDateTime.cc
//
// HtDateTime: Parse, split, compare and format dates and times.
//  	       Uses locale.
//
// Part of the ht://Dig package   <http://www.htdig.org/>
// Copyright (c) 1999-2003 The ht://Dig Group
// For copyright details, see the file COPYING in your distribution
// or the GNU General Public License version 2 or later 
// <http://www.gnu.org/copyleft/gpl.html>
//
// $Id: HtDateTime.cc,v 1.8 2003-06-20 16:47:30 mnencia Exp $
//

#ifdef HAVE_CONFIG_H
#include "htconfig.h"
#endif /* HAVE_CONFIG_H */

#include "HtDateTime.h"

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

#ifdef HAVE_STD
#include <iostream>
#ifdef HAVE_NAMESPACES
using namespace std;
#endif
#else
#include <iostream.h>
#endif /* HAVE_STD */

#ifndef HAVE_STRPTIME
// mystrptime() declared in lib.h, defined in htlib/strptime.cc
#define strptime(s,f,t)	mystrptime(s,f,t)
#else /* HAVE_STRPTIME */
#ifndef HAVE_STRPTIME_DECL
extern "C" {
extern char *strptime(const char *__s, const char *__fmt, struct tm *__tp);
}
#endif /* HAVE_STRPTIME_DECL */
#endif /* HAVE_STRPTIME */

///////
   //    Static local variable : Visible only here !!!
///////

#define MAXSTRTIME 256  // Max length of _strtime

static struct tm Ht_tm;
static char _strtime[MAXSTRTIME];


///////
  //     Recognized Date Formats
///////

//     RFC1123: Sun, 06 Nov 1994 08:49:37 GMT
#define RFC1123_FORMAT "%a, %d %b %Y %H:%M:%S %Z"
#define LOOSE_RFC1123_FORMAT "%d %b %Y %H:%M:%S"

//     RFC850 : Sunday, 06-Nov-94 08:49:37 GMT
#define RFC850_FORMAT  "%A, %d-%b-%y %H:%M:%S %Z"
#define LOOSE_RFC850_FORMAT  "%d-%b-%y %H:%M:%S"

//     ANSI C's asctime() format : Sun Nov  6 08:49:37 1994
#define ASCTIME_FORMAT  "%a %b %e %H:%M:%S %Y"
#define LOOSE_ASCTIME_FORMAT  "%b %e %H:%M:%S %Y"

// 	  ISO8601 : 1994-11-06 08:49:37 GMT
#define ISO8601_FORMAT "%Y-%m-%d %H:%M:%S %Z"

// 	  ISO8601 (short version): 1994-11-06
#define ISO8601_SHORT_FORMAT "%Y-%m-%d"

// 	  Timestamp : 19941106084937
#define TIMESTAMP_FORMAT "%Y%m%d%H%M%S"



///////
   //   Initialization
///////

const int HtDateTime::days[] = {	31, 28, 31, 30, 31, 30,
				31, 31, 30, 31, 30, 31};


///////   //   Input Formats   //   ///////

///////
   //   Generalized date/time parser for "LOOSE" formats
   //   - converts LOOSE RFC850 or RFC1123 date string into a time value
   //   - converts SHORT ISO8601 date string into a time value
   //   - autodetects which of these formats is used
   //   - assumes midnight if time portion omitted
   //   We've had problems using strptime() and timegm() on a few platforms
   //   while parsing these formats, so this is an attempt to sidestep them.
   //
   //	Returns 0 if parsing failed, or returns number of characters parsed
   //	in date string otherwise, and sets Ht_t field to time_t value.
///////
#define EPOCH	1970

int HtDateTime::Parse(const char *date)
{
    register const char *s;
    register const char *t;
    int		day, month, year, hour, minute, second;

    //
    // Three possible time designations:
    //      Tuesday, 01-Jul-97 16:48:02 GMT     (RFC850)
    // or
    //      Thu, 01 May 1997 00:40:42 GMT       (RFC1123)
    // or
    //      1997-05-01 00:40:42 GMT             (ISO8601)
    //
    // We strip off the weekday because we don't need it, and
    // because some servers send invalid weekdays!
    // (Some don't even send a weekday, but we'll be flexible...)

    s = date;
    while (*s && *s != ',')
	s++;
    if (*s)
	s++;
    else
	s = date;
    while (isspace(*s))
	s++;

    // check for ISO8601 format
    month = 0;
    t = s;
    while (isdigit(*t))
	t++;
    if (t > s && *t == '-' && isdigit(t[1]))
	day = -1;
    else {
	// not ISO8601, so try RFC850 or RFC1123
	// get day...
	if (!isdigit(*s))
	    return 0;
	day = 0;
	while (isdigit(*s))
	    day = day * 10 + (*s++ - '0');
	if (day > 31)
	    return 0;
	while (*s == '-' || isspace(*s))
	    s++;

	// get month...
	// (it's ugly, but it works)
	switch (*s++) {
	    case 'J': case 'j':
		switch (*s++) {
		case 'A': case 'a':
		    month = 1;
		    s++;
		    break;
		case 'U': case 'u':
		    switch (*s++) {
		    case 'N': case 'n':
			month = 6;
			break;
		    case 'L': case 'l':
			month = 7;
			break;
		    default:
			return 0;
		    }
		    break;
		default:
		    return 0;
		}
		break;
	    case 'F': case 'f':
		month = 2;
		s += 2;
		break;
	    case 'M': case 'm':
		switch (*s++) {
		case 'A': case 'a':
		    switch (*s++) {
		    case 'R': case 'r':
			month = 3;
			break;
		    case 'Y': case 'y':
			month = 5;
			break;
		    default:
			return 0;
		    }
		    break;
		default:
		    return 0;
		}
		break;
	    case 'A': case 'a':
		switch (*s++) {
		case 'P': case 'p':
		    month = 4;
		    s++;
		    break;
		case 'U': case 'u':
		    month = 8;
		    s++;
		    break;
		default:
		    return 0;
		}
		break;
	    case 'S': case 's':
		month = 9;
		s += 2;
		break;
	    case 'O': case 'o':
		month = 10;
		s += 2;
		break;
	    case 'N': case 'n':
		month = 11;
		s += 2;
		break;
	    case 'D': case 'd':
		month = 12;
		s += 2;
		break;
	    default:
		return 0;
	}
	while (*s == '-' || isspace(*s))
	    s++;
    }

    // get year...
    if (!isdigit(*s))
	return 0;
    year = 0;
    while (isdigit(*s))
	year = year * 10 + (*s++ - '0');
    if (year < 69)
	year += 2000;
    else if (year < 1900)
	year += 1900;
    else if (year >= 19100)	// seen some programs do it, why not check?
	year -= (19100-2000);
    while (*s == '-' || isspace(*s))
	s++;

    if (day < 0) {		// still don't have day, so it's ISO8601 format
	// get month...
	if (!isdigit(*s))
	    return 0;
	month = 0;
	while (isdigit(*s))
	    month = month * 10 + (*s++ - '0');
	if (month < 1 || month > 12)
	    return 0;
	while (*s == '-' || isspace(*s))
	    s++;

	// get day...
	if (!isdigit(*s))
	    return 0;
	day = 0;
	while (isdigit(*s))
	    day = day * 10 + (*s++ - '0');
	if (day < 1 || day > 31)
	    return 0;
	while (*s == '-' || isspace(*s))
	    s++;
    }

    // optionally get hour...
    hour = 0;
    while (isdigit(*s))
	hour = hour * 10 + (*s++ - '0');
    if (hour > 23)
	return 0;
    while (*s == ':' || isspace(*s))
	s++;

    // optionally get minute...
    minute = 0;
    while (isdigit(*s))
	minute = minute * 10 + (*s++ - '0');
    if (minute > 59)
	return 0;
    while (*s == ':' || isspace(*s))
	s++;

    // optionally get second...
    second = 0;
    while (isdigit(*s))
	second = second * 10 + (*s++ - '0');
    if (second > 59)
	return 0;
    while (*s == ':' || isspace(*s))
	s++;

    // Assign the new value to time_t field
    //
    // Calculate date as seconds since 01 Jan 1970 00:00:00 GMT
    // This is based somewhat on the date calculation code in NetBSD's
    // cd9660_node.c code, for which I was unable to find a reference.
    // It works, though!
    //
    Ht_t = (time_t) (((((367L*year - 7L*(year+(month+9)/12)/4
				   - 3L*(((year)+((month)+9)/12-1)/100+1)/4
				   + 275L*(month)/9 + day) -
			(367L*EPOCH - 7L*(EPOCH+(1+9)/12)/4
				   - 3L*((EPOCH+(1+9)/12-1)/100+1)/4
				   + 275L*1/9 + 1))
		       * 24 + hour) * 60 + minute) * 60 + second);

    // cerr << "Date string '" << date << "' converted to time_t "
    //      << (int)Ht_t << ", used " << (s-date) << " characters\n";

    return s-date;
}

///////
   //   Personalized format such as C strftime function
  //     Overloaded version 1
   //    It ignores, for now, Time Zone values
///////

char *HtDateTime::SetFTime(const char *buf, const char *format)
{

   register char *p;
   register int r;

   ToGMTime();    // This must be set cos strptime always stores in GM 

   p = (char *) buf;
   if (*format == '%')		// skip any unexpected white space
      while (isspace(*p))
	 p++;

   // Special handling for LOOSE/SHORT formats...
   if ((strcmp((char *) format, LOOSE_RFC850_FORMAT) == 0 ||
	strcmp((char *) format, LOOSE_RFC1123_FORMAT) == 0 ||
	strcmp((char *) format, ISO8601_SHORT_FORMAT) == 0)  &&
       (r = Parse(p)) > 0)
	    return p+r;

   p = (char *) strptime (p, (char *) format, & Ht_tm);

#ifdef TEST_HTDATETIME
//   ViewStructTM(& Ht_tm);
#endif
   
   // Assign the new value to time_t value
   SetDateTime(Ht_tm);

   return p;
   
}


///////
   //   C asctime() standard format
///////

void HtDateTime::SetAscTime(char *s)
{

   // Unfortunately, I cannot think of an easy test to 
   // see if we have a weekday *FIX*
   SetFTime(s, ASCTIME_FORMAT);

}

///////
   //   RFC1123 standard Date format
 //     Sun, 06 Nov 1994 08:49:37 GMT
///////

void HtDateTime::SetRFC1123(char *s)
{

   // abbreviated weekday name;
   // day of the month;
   // abbreviated month name;
   // year as ccyy;
   // hour ( 00 - 23);
   // minute ( 00 - 59);
   // seconds ( 00 - 59);
   // time zone name;

   // First, if we have it, strip off the weekday
   char *stripped;
   stripped = strchr(s, ',');
   if (stripped)
        stripped++;
   else
        stripped = s;

   SetFTime(stripped, LOOSE_RFC1123_FORMAT);

}


///////
   //   RFC850 standard Date format
 //     Sunday, 06-Nov-1994 08:49:37 GMT
///////

void HtDateTime::SetRFC850(char *s)
{

   // weekday name;
   // day of the month;
   // abbreviated month name;
   // year within century;
   // hour ( 00 - 23);
   // minute ( 00 - 59);
   // seconds ( 00 - 59);
   // time zone name;

   // First, if we have it, strip off the weekday
   char *stripped;
   stripped = strchr(s, ',');
   if (stripped)
        stripped++;
   else
        stripped = s;

   SetFTime(stripped, LOOSE_RFC850_FORMAT);

}


///////
   //   ISO8601 standard Date format
 //     1994-11-06 08:49:37 GMT
///////

void HtDateTime::SetISO8601(char *s)
{

   // year as ccyy;
   // month ( 01 - 12)
   // day of the month
   // hour ( 00 - 23)
   // minute ( 00 - 59)
   // seconds ( 00 - 59);
   // time zone name;

   SetFTime(s, ISO8601_FORMAT);
   
}


///////
   //   Timestamp Date format (MySQL) without timezone
 //     19941106084937
///////

void HtDateTime::SetTimeStamp(char *s)
{

   // year as ccyy;
   // month ( 01 - 12)
   // day of the month
   // hour ( 00 - 23)
   // minute ( 00 - 59)
   // seconds ( 00 - 59);

   SetFTime(s, TIMESTAMP_FORMAT);
   
}


///////
   //   Default date and time format for the locale
///////

void HtDateTime::SetDateTimeDefault(char *s)
{

   SetFTime(s, "%c");
   
}




///////   //   Output Formats   //   ///////


///////
   //   Personalized format such as C strftime function
  //     Overloaded version 1
///////

size_t HtDateTime::GetFTime(char *s, size_t max, const char *format) const
{
   // Refresh static struct tm variable

   RefreshStructTM();

   return strftime(s, max, format, & Ht_tm);

}

///////
   //   Personalized format such as C strftime function
  //     Overloaded version 2 - The best to be used outside
   //       	   	       	  for temporary uses
///////

char *HtDateTime::GetFTime(const char *format) const
{

   // Invoke GetFTime overloaded method
   
   if(GetFTime(_strtime, MAXSTRTIME, format))
      return (char *)_strtime;
   else return 0;

}

///////
   //   RFC1123 standard Date format
 //     Sun, 06 Nov 1994 08:49:37 GMT
///////

char *HtDateTime::GetRFC1123() const
{

   // abbreviated weekday name;
   // day of the month;
   // abbreviated month name;
   // year as ccyy;
   // hour ( 00 - 23);
   // minute ( 00 - 59);
   // seconds ( 00 - 59);
   // time zone name;

   GetFTime(_strtime, MAXSTRTIME, RFC1123_FORMAT);

   return (char *)_strtime;
   
}

///////
   //   RFC850 standard Date format
 //     Sunday, 06-Nov-94 08:49:37 GMT
///////

char *HtDateTime::GetRFC850() const
{

   // full weekday name
   // day of the month
   // abbreviated month name
   // year within century ( 00 - 99 )
   // hour ( 00 - 23)
   // minute ( 00 - 59)
   // seconds ( 00 - 59);
   // time zone name;

   GetFTime(_strtime, MAXSTRTIME, RFC850_FORMAT);

   return (char *)_strtime;
   
}

///////
   //   C asctime() standard format
///////

char *HtDateTime::GetAscTime() const
{

   GetFTime(_strtime, MAXSTRTIME, ASCTIME_FORMAT);
   return (char *)_strtime;

}

///////
   //   ISO8601 standard Date format
 //     1994-11-06 08:49:37 GMT
///////

char *HtDateTime::GetISO8601() const
{

   // year as ccyy;
   // month ( 01 - 12)
   // day of the month
   // hour ( 00 - 23)
   // minute ( 00 - 59)
   // seconds ( 00 - 59);
   // time zone name;

   GetFTime(_strtime, MAXSTRTIME, ISO8601_FORMAT);

   return (char *)_strtime;
   
}

///////
   //   ISO8601 standard Date format
 //     1994-11-06 08:49:37 GMT
///////

char *HtDateTime::GetShortISO8601() const
{

   // year as ccyy;
   // month ( 01 - 12)
   // day of the month

   GetFTime(_strtime, MAXSTRTIME, ISO8601_SHORT_FORMAT);

   return (char *)_strtime;
   
}

///////
   //   Timestamp Date format (MySQL) without timezone
 //     19941106084937
///////

char *HtDateTime::GetTimeStamp() const
{

   // year as ccyy;
   // month ( 01 - 12)
   // day of the month
   // hour ( 00 - 23)
   // minute ( 00 - 59)
   // seconds ( 00 - 59);

   GetFTime(_strtime, MAXSTRTIME, TIMESTAMP_FORMAT);

   return (char *)_strtime;
   
}

///////
   //   Default date and time format for the locale
///////

char *HtDateTime::GetDateTimeDefault() const
{

   GetFTime(_strtime, MAXSTRTIME, "%c");

   return (char *)_strtime;
   
}

///////
   //   Default date format for the locale
///////

char *HtDateTime::GetDateDefault() const
{

   GetFTime(_strtime, MAXSTRTIME, "%x");

   return (char *)_strtime;
   
}

///////
   //   Default time format for the locale
///////

char *HtDateTime::GetTimeDefault() const
{

   GetFTime(_strtime, MAXSTRTIME, "%X");

   return (char *)_strtime;
   
}



///////
   //   Set the static struct tm depending on localtime status
///////


void HtDateTime::RefreshStructTM() const
{

   if(local_time)
      // Setting localtime
   	 memcpy(& Ht_tm, localtime(&Ht_t), sizeof(struct tm));
   else
   	 // Setting UTC or GM time
   	 memcpy(& Ht_tm , gmtime(&Ht_t), sizeof(struct tm));
	 
}


// Set the date time from a struct tm pointer

void HtDateTime::SetDateTime(struct tm *ptm)
{

   if(local_time)
   	 Ht_t = mktime(ptm);  	// Invoke mktime
   else
   	 Ht_t = HtTimeGM(ptm);	// Invoke timegm alike function
   
}


// Set time to now

void HtDateTime::SettoNow()
{
   Ht_t = time(0);
}


// Sets date by passing specific values
// The values are reffered to the GM date time
// Return false if failed

bool HtDateTime::SetGMDateTime ( int year, int mon, int mday,
			int hour, int min, int sec)
{
   struct tm tm_tmp;

   // Year
   
   if ( ! isAValidYear (year) ) return false;

   if( year < 100)
	 year=Year_From2To4digits (year); // For further checks it's converted

   // Assigning the year
   
   tm_tmp.tm_year=year-1900;


   // Month

   if( ! isAValidMonth(mon) ) return false;

   tm_tmp.tm_mon=mon-1; // Assigning the month to the structure


   // Day

   if ( ! isAValidDay ( mday, mon, year ) ) return false;
   
   tm_tmp.tm_mday=mday; // Assigning the day of the month



   if(hour >= 0 && hour < 24) tm_tmp.tm_hour = hour;
   	 else return false;	
	
   if(min >= 0 && min < 60) tm_tmp.tm_min = min;
   	 else return false;	
	
   if(sec >= 0 && sec < 60) tm_tmp.tm_sec = sec;
   	 else return false;	

   tm_tmp.tm_yday = 0; // day of the year (to be ignored)
   tm_tmp.tm_isdst = 0; // default for GM (to be ignored)

   // Now we are going to insert the new values as time_t value
   // This can only be done using GM Time and so ...

   if (isLocalTime())
   {
   	 ToGMTime(); 	       // Change to GM Time
   	 SetDateTime(&tm_tmp); // commit it
	 ToLocalTime();       // And then return to Local Time
   }
   else SetDateTime(&tm_tmp); // only commit it

   return true;
   
}


///////
   //   Gets a struct tm from the value stored in the object
  //    It's a protected method. Not visible outside the class 
///////

struct tm &HtDateTime::GetStructTM() const 
{
	RefreshStructTM(); // refresh it

	return Ht_tm;
}


struct tm &HtDateTime::GetGMStructTM() const 
{
   GetGMStructTM (Ht_tm);
   return Ht_tm;
}

void HtDateTime::GetGMStructTM(struct tm & t) const
{
   // Directly gets gmtime value
   memcpy(& t , gmtime(& Ht_t), sizeof(struct tm));
}


///////
   //   Is a leap year?
///////

bool HtDateTime::LeapYear (int y)
{

   if(y % 400 == 0 || ( y % 100 != 0 && y % 4 == 0))
   	 return true; 	// a leap year
   else
   	 return false;	// and not
}


///////
   //   Is a valid year number?
///////

bool HtDateTime::isAValidYear (int y)
{

   if(y >= 1970 && y < 2069) return true; // simple check and most likely

   if(y >= 0 && y < 100) return true;  // 2 digits year number
   
   return false;
   
}


///////
   //   Is a valid month number?
///////

bool HtDateTime::isAValidMonth (int m)
{

   if( m >= 1 && m <= 12) return true;
   else return false;
   
}


///////
   //   Is a valid day?
///////

bool HtDateTime::isAValidDay (int d, int m, int y)
{

   if ( ! isAValidYear (y) ) return false;	// Checks for the year

   if ( ! isAValidMonth (m) ) return false;	// Checks for the month
   
   if(m == 2)
   {
   
   	 // Expands the 2 digits year number
   	 if ( y < 100 ) y=Year_From2To4digits(y);
	 
	 if ( LeapYear (y) ) // Checks for the leap year
      {
	    if (d >= 1 && d <= 29) return true;
	    else return false;
      }
   }

   // Acts as default
   
   if (d >= 1 && d <= days [m -1]) return true;
   else return false;
   
}


///////
   //   Comparison methods
///////


int HtDateTime::DateTimeCompare (const HtDateTime & right) const
{
   int result;
   
   // Let's compare the date

   result=DateCompare(right);
   
   if(result) return result;
   
   // Same date. Let's compare the time

   result=TimeCompare(right);

   return result; // Nothing more to check

}


int HtDateTime::GMDateTimeCompare (const HtDateTime & right) const
{
	// We must compare the whole time_t value
	
	if ( * this > right) return 1;	 // 1st greater than 2nd
	if ( * this < right) return 1;	 // 1st lower than 2nd
	
	return 0;
	
}


int HtDateTime::DateCompare (const HtDateTime & right) const
{

   // We must transform them in 2 struct tm variables
	
   struct tm tm1, tm2;
   
   this->GetGMStructTM (tm1);
   right.GetGMStructTM (tm2);

   // Let's compare them
   return DateCompare (&tm1, &tm2);
	
}


int HtDateTime::GMDateCompare (const HtDateTime & right) const
{

   // We must transform them in 2 struct tm variables
   // both referred to GM time
	
   struct tm tm1, tm2;
   
   this->GetGMStructTM (tm1);
   right.GetGMStructTM (tm2);

   // Let's compare them
   return DateCompare (&tm1, &tm2);
	
}


int HtDateTime::TimeCompare (const HtDateTime & right) const
{

   // We must transform them in 2 struct tm variables
	
   struct tm tm1, tm2;
	
   this->GetStructTM (tm1);
   right.GetStructTM (tm2);

   return TimeCompare (&tm1, &tm2);
	
}


int HtDateTime::GMTimeCompare (const HtDateTime & right) const
{

   // We must transform them in 2 struct tm variables
	
   struct tm tm1, tm2;

   // We take the GM value of the time	
   this->GetGMStructTM (tm1);
   right.GetGMStructTM (tm2);

   return TimeCompare (&tm1, &tm2);
	
}



///////
   //   Static methods of comparison between 2 struct tm pointers
///////


///////
   //   Compares only the date (ignoring the time)
///////

int HtDateTime::DateCompare(const struct tm *tm1, const struct tm *tm2)
{

   // Let's check the year
   
   if (tm1->tm_year < tm2->tm_year) return -1;
   if (tm1->tm_year > tm2->tm_year) return  1;

   // Same year. Let's check the month
   if (tm1->tm_mon < tm2->tm_mon) return -1;
   if (tm1->tm_mon > tm2->tm_mon) return 1;
   
   // Same month. Let's check the day of the month
   
   if (tm1->tm_mday < tm2->tm_mday) return -1;
   if (tm1->tm_mday > tm2->tm_mday) return 1;

   // They are equal for the date
   return 0;
}


///////
   //   Compares only the time (ignoring the date)
///////

int HtDateTime::TimeCompare(const struct tm *tm1, const struct tm *tm2)
{

   // Let's check the hour
   
   if (tm1->tm_hour < tm2->tm_hour) return -1;
   if (tm1->tm_hour > tm2->tm_hour) return  1;

   // Same hour . Let's check the minutes
   
   if (tm1->tm_min < tm2->tm_min) return -1;
   if (tm1->tm_min > tm2->tm_min) return 1;

   // Ooops !!! Same minute. Let's check the seconds
   
   if (tm1->tm_sec < tm2->tm_sec) return -1;
   if (tm1->tm_sec > tm2->tm_sec) return 1;

   // They are equal for the time
   return 0;
}


///////
   //   Compares both date and time
///////

int HtDateTime::DateTimeCompare(const struct tm *tm1, const struct tm *tm2)
{

   int compare_date = DateCompare(tm1, tm2);
   
   if(compare_date) return compare_date;  // Different days

   // We are in the same day. Let's check the time

   int compare_time = TimeCompare(tm1, tm2);
   if(compare_time) return compare_time;  // Different time
   
   // Equal
   return 0;
}

time_t HtDateTime::HtTimeGM (struct tm *tm)
{

#if HAVE_TIMEGM
   return timegm (tm);
#else
   return Httimegm (tm); // timegm replacement in timegm.c
   // static time_t gmtime_offset;
   // tm->tm_isdst = 0;
   // return __mktime_internal (tm, gmtime, &gmtime_offset);
#endif
   
}



// Returns the difference in seconds between two HtDateTime Objects

int HtDateTime::GetDiff(const HtDateTime &d1, const HtDateTime &d2)
{

   return (int) ( d1.Ht_t - d2.Ht_t );
   
}






///////
   //   Only for test and debug
///////

#ifdef TEST_HTDATETIME


///////
   //   View of struct tm fields
///////

void HtDateTime::ViewStructTM()
{
   // Default viewing: refresh depending on time_t value

   RefreshStructTM(); // Refresh static variable

   ViewStructTM(&Ht_tm);
}

void HtDateTime::ViewStructTM(struct tm *ptm)
{

	cout << "Struct TM fields" << endl;
	cout << "================" << endl;
	cout << "tm_sec   :\t" << ptm->tm_sec << endl;
	cout << "tm_min   :\t" << ptm->tm_min  << endl;
	cout << "tm_hour  :\t" << ptm->tm_hour << endl;
	cout << "tm_mday  :\t" << ptm->tm_mday << endl;
	cout << "tm_mon   :\t" << ptm->tm_mon  << endl;
	cout << "tm_year  :\t" << ptm->tm_year << endl;
	cout << "tm_wday  :\t" << ptm->tm_wday << endl;
	cout << "tm_yday  :\t" << ptm->tm_yday << endl;
	cout << "tm_isdst :\t" << ptm->tm_isdst<< endl;

}




int HtDateTime::Test(void)
{

   int ok=1;
   
   const char *test_dates[] = 
   {
    "1970.01.01 00:00:00",
    "1970.01.01 00:00:01",
    "1972.02.05 23:59:59",
    "1972.02.28 00:59:59",
    "1972.02.28 23:59:59",
    "1972.02.29 00:00:00",
    "1972.03.01 13:00:04",
    "1973.03.01 12:00:00",
    "1980.01.01 00:00:05",
    "1984.12.31 23:00:00",
    "1997.06.05 17:55:35",
    "1999.12.31 23:00:00",
    "2000.01.01 00:00:05",
    "2000.02.28 23:00:05",
    "2000.02.29 23:00:05",
    "2000.03.01 00:00:05",
    "2007.06.05 17:55:35",
    "2038.01.19 03:14:07",
    0
   };

   const char *test_dates_ISO8601[] = 
   {
    "1970-01-01 00:00:00 GMT",
    "1970-01-01 00:00:00 CET",
    "1990-02-27 23:30:20 GMT",
    "1999-02-28 06:53:40 GMT",
    "1975-04-27 06:53:40 CET",
    0
   };

   const char *test_dates_RFC1123[] = 
   {
   "Sun, 06 Nov 1994 08:49:37 GMT",
   "Sun, 25 Apr 1999 17:49:37 GMT",
   "Sun, 25 Apr 1999 17:49:37 CET",
    0
   };
  
   const char *test_dates_RFC850[] = 
   {
   "Sunday, 06-Nov-94 08:49:37 GMT",
   "Sunday, 25-Apr-99 17:49:37 GMT",
   "Sunday, 25-Apr-99 17:49:37 CET",
    0
   };
  

   const char myformat[]="%Y.%m.%d %H:%M:%S";

   // Tests a personal format  
   
   cout << endl << "Beginning Test of a personal format such as "
   	       	   << myformat << endl << endl;
   
   if (Test((char **)test_dates, (const char *)myformat))
   	 cout << "Test OK." << endl;
   else
   {
   	 cout << "Test Failed." << endl;
	 ok=0;
   }


   // Tests ISO 8601 Format

   cout << endl << "Beginning Test of ISO 8601 format" << endl << endl;

   if(Test((char **)test_dates_ISO8601, (const char *)ISO8601_FORMAT))
   	 cout << "Test OK." << endl;
   else
   {
   	 cout << "Test Failed." << endl;
	 ok=0;
   }


   // Tests RFC 1123 Format

   cout << endl << "Beginning Test of RFC 1123 format" << endl << endl;

   if (Test((char **)test_dates_RFC1123, (const char *)RFC1123_FORMAT))
   	 cout << "Test OK." << endl;
   else
   {
   	 cout << "Test Failed." << endl;
	 ok=0;
   }


   // Tests RFC 850 Format

   cout << endl << "Beginning Test of RFC 850 format" << endl << endl;

   if (Test((char **)test_dates_RFC850, (const char *)RFC850_FORMAT))
   	 cout << "Test OK." << endl;
   else
   {
   	 cout << "Test Failed." << endl;
	 ok=0;
   }


   return(ok ? 1 : 0);
  
}



int HtDateTime::Test(char **test_dates, const char *format)
{
  int i, ok = 1;
  HtDateTime orig, conv;

  for (i = 0; (test_dates[i]); i++)
  {
  
      cout << "\t " << i+1 << "\tDate string parsing of:" << endl;
	 cout << "\t\t" << test_dates[i] << endl;
	 cout << "\t\tusing format: " << format << endl << endl;

   	 orig.SetFTime(test_dates[i], format);
   
   	 orig.ComparisonTest(conv);
	 
      conv=orig;
   
      if (orig != conv)
   	 {
   	    cout << "HtDateTime test failed!" << endl;
	    cout << "\t Original : " << orig.GetRFC1123() << endl;
	    cout << "\t Converted: " << orig.GetRFC1123() << endl;
         ok = 0;
   	 }
   	 else
   	 {
	    orig.ToLocalTime();
	    cout << endl << "\t   Localtime viewing" << endl;
	    orig.ViewFormats();
	    orig.ToGMTime();
	    cout << endl << "\t   GMtime viewing" << endl;
	    orig.ViewFormats();
   	    //orig.ViewStructTM();
      }
	 
	 cout << endl;
	 
   }
   
   return ok;
}


void HtDateTime::ComparisonTest (const HtDateTime &right) const
{
   int result;
   
   
   cout << "Comparison between:" << endl;
   
   cout << " 1. " << this->GetRFC1123() << endl;
   cout << " 2. " << right.GetRFC1123() << endl;
   cout << endl;


///////
   //   Complete comparison
///////
   
      cout << "\tComplete comparison (date and time)" << endl;
      result = this->DateTimeCompare (right);

   cout << "\t\t " << this->GetDateTimeDefault();
   
   if (result > 0 )
   	 cout << " is greater than ";
   else if (result < 0 )
   	 cout << " is lower than ";
	 else cout << " is equal to ";

   cout << " " << right.GetDateTimeDefault() << endl;



///////
   //   Date comparison
///////
   
      cout << "\tDate comparison (ignoring time)" << endl;
      result = this->DateCompare (right);

   cout << "\t\t " << this->GetDateDefault();
   
   if (result > 0 )
   	 cout << " is greater than ";
   else if (result < 0 )
   	 cout << " is lower than ";
	 else cout << " is equal to ";

   cout << " " << right.GetDateDefault() << endl;


///////
   //   Date comparison (after GM time conversion)
///////
   
      cout << "\tDate comparison (ignoring time) - GM time conversion" << endl;
      result = this->GMDateCompare (right);

   cout << "\t\t " << this->GetDateDefault();
   
   if (result > 0 )
   	 cout << " is greater than ";
   else if (result < 0 )
   	 cout << " is lower than ";
	 else cout << " is equal to ";

   cout << " " << right.GetDateDefault() << endl;


	 
///////
   //   Time comparison
///////
   
      cout << "\tTime comparison (ignoring date)" << endl;
      result = this->TimeCompare (right);

   cout << "\t\t " << this->GetTimeDefault();
   
   if (result > 0 )
   	 cout << " is greater than ";
   else if (result < 0 )
   	 cout << " is lower than ";
	 else cout << " is equal to ";

   cout << " " << right.GetTimeDefault() << endl;


///////
   //   Time comparison (after GM time conversion)
///////
   
      cout << "\tTime comparison (ignoring date) - GM time conversion" << endl;
      result = this->GMTimeCompare (right);

   cout << "\t\t " << this->GetTimeDefault();
   
   if (result > 0 )
   	 cout << " is greater than ";
   else if (result < 0 )
   	 cout << " is lower than ";
	 else cout << " is equal to ";

   cout << " " << right.GetTimeDefault() << endl;
   
}



void HtDateTime::ViewFormats()
{

   cout << "\t\t RFC 1123 Format : " << GetRFC1123() << endl;
   cout << "\t\t RFC 850 Format  : " << GetRFC850() << endl;
   cout << "\t\t C Asctime Format: " << GetAscTime() << endl;
   cout << "\t\t ISO 8601 Format : " << GetISO8601() << endl;

}

#endif
