//
// Copyright (C) 1991 Texas Instruments Incorporated.
//
// Permission is granted to any individual or institution to use, copy, modify,
// and distribute this software, provided that this complete copyright and
// permission notice is maintained, intact, in all copies and supporting
// documentation.
//
// Texas Instruments Incorporated provides this software "as is" without
// express or implied warranty.
//
// Created: MBN 04/11/89 -- Initial design and implementation
// Updated: MNF 07/25/89 -- Add the parse member function 
// Updated: MBN 09/05/89 -- Added conditional exception handling
// Updated: MBN 01/03/90 -- Adjusted strfind() calls to match new syntax
// Updated: MBN 01/17/90 -- Fixed parsing algorithms to be GMT relative
// Updated: MBN 02/06/90 -- Support years prior to the epoch (1/1/1970)
// Updated: MBN 02/12/90 -- Changed all ascii() functions to return const char*
// Updated: MBN 02/13/90 -- Made ascii_duration() a member function for epoch
// Updated: MJF 03/12/90 -- Added group names to RAISE
// Updated: MJF 01/17/91 -- Fixed parse to use ANSI function mktime()
// Updated: DAN 01/21/91 -- Added adjust_dst() for OS/2 DST adjustment.
//
// This file  contains member and friend function  implementation  code for the
// Date_Time class defined  in the Date_Time.h header file.   Where appropriate
// and possible, interfaces to,  and us of, existing  system functions has been
// incorporated.  An overview  of the Date_Time   class structor  along with  a
// synopsis of each member and friend function, can be found in the Date_Time.h
// header file.  Two static  char* tables are defined  within  this  file to be
// used only by member functions of the Date_Time class.  The first is the date
// format table that is indexed by the enum  symbolic values in the <country.h>
// header  file.  This table contains  format  strings for  sscanf()   used  to
// implement the  formatted  ASCII  date output according  to  the rules of the
// appropriate country.  The second is the time table that is  again indexed by
// the enum symbolic values  in the <country.h> header  file and   performs the
// same function for sscanf() output of the time.

#ifndef DATETIMEH		// If DateTime class has not been defined
#include <cool/Date_Time.h>	// Include class specification header file
#endif

#if defined(DOS)
extern "C" {
#include <ctype.h>		// ANSI C character macros
#include <stdlib.h>		// Include standard c library support
}
#else
#include <ctype.h>		// ANSI C character macros
#include <stdlib.h>		// Include standard c library support
#endif

#if !defined(DOS)               // define ANSI C routine not on SUN
extern "C" {
extern long timelocal (struct tm*);
long mktime (struct tm* tmptr) {  
   return timelocal(tmptr);   
}
}
#endif

#if defined(DOS)
void adjust_dst (struct tm* t) {
  // OS2 mktime increments the hour by one if daylight savings time is in effect
  // we have to roll everything back one to correct for this.
  if (t->tm_isdst) { 
    // if 12am, roll back to 11 pm of previous day
    if (t->tm_hour == 0) { 
      t->tm_hour = 23;
      // if the 1st, roll day back to last of previous month
      if (t->tm_mday == 1) { 
	// if Jan, roll month back to Dec and the year back one
	if (t->tm_mon==0) {
	  t->tm_mon = 11;
	  t->tm_year--;
	}
	if (t->tm_mon==0 || t->tm_mon==2 || t->tm_mon==4 || t->tm_mon==6 ||
	    t->tm_mon==7 || t->tm_mon==9 || t->tm_mon==11) t->tm_mday = 31;
	else if (t->tm_mon==3 || t->tm_mon==5 || t->tm_mon==8 ||
		 t->tm_mon==10) t->tm_mday = 30;
	else if (t->tm_mon==1 && IS_LEAP_YEAR(t->tm_year)) t->tm_mday = 29;
	else t->tm_mday = 28;
      }
      else t->tm_mday--;
      // roll back day of week
      if (t->tm_wday == 0) t->tm_wday = 6;
      else t->tm_wday--;
      // roll back day of year
      if (t->tm_yday==0) {
        if (IS_LEAP_YEAR(t->tm_year-1)) t->tm_yday = 366;
        else t->tm_yday = 365;}
      else t->tm_yday--;
    }
    else t->tm_hour--;
  }
}
#endif


static const char* date_format_s[] = {		// Date formatting table
"* UNKNOWN *",				// Unknown country code
"%02d-%02d-%4d",				// 03-29-1989 UNITED STATES   
"%4d-%02d-%02d",				// 1989-29-03 FRENCH CANADIAN 
"%02d-%02d/%4d",				// 03/29/1989 LATIN AMERICA   
"%02d-%02d-%4d",				// 03-29-1989 NETHERLANDS     
"%02d-%02d/%4d",				// 03/29/1989 BELGIUM         
"%02d-%02d/%4d",				// 03/29/1989 FRANCE          
"%02d-%02d/%4d",				// 03/29/1989 SPAIN           
"%02d-%02d/%4d",				// 03/29/1989 ITALY           
"%02d-%02d.%4d",				// 03.29.1989 SWITZERLAND     
"%02d-%02d-%4d",				// 29-03-1989 UNITED KINGDOM  
"%02d-%02d/%4d",				// 03/29/1989 DENMARK         
"%4d-%02d-%02d",				// 1989-29-03 SWEDEN          
"%02d/%02d/%4d",				// 03/29/1989 NORWAY          
"%02d.%02d.%4d",				// 03.29.1989 GERMANY         
"%02d/%02d/%4d",				// 03/29/1989 PORTUGAL        
"%02d.%02d.%4d",				// 03.29.1989 FINLAND         
"%02d/%02d/%4d",				// 03/29/1989 ARABIC COUNTRIES
"%02d %02d %4d"				// 03 29 1989 ISRAEL          
};

static const char* time_format_s[] = {		// Time formatting table
"* UNKNOWN *",				// Unknown country code
"%02d:%02d:%02d %s",				// 17:35:00 UNITED STATES   
"%02d:%02d:%02d %s",				// 17:35:00 FRENCH CANADIAN 
"%02d:%02d:%02d %s",				// 17:35:00 LATIN AMERICA   
"%02d:%02d:%02d %s",				// 17:35:00 NETHERLANDS     
"%02d:%02d:%02d %s",				// 17:35:00 BELGIUM         
"%02d:%02d:%02d %s",				// 17:35:00 FRANCE          
"%02d:%02d:%02d %s",				// 17:35:00 SPAIN           
"%02d:%02d:%02d %s",				// 17:35:00 ITALY           
"%02d.%02d.%02d %s",				// 17.35.00 SWITZERLAND     
"%02d:%02d:%02d %s",				// 17:35:00 UNITED KINGDOM  
"%02d:%02d:%02d %s",				// 17:35:00 DENMARK         
"%02d.%02d.%02d %s",				// 17.35.00 SWEDEN          
"%02d:%02d:%02d %s",				// 17:35:00 NORWAY          
"%02d.%02d.%02d %s",				// 17.35.00 GERMANY         
"%02d:%02d:%02d %s",				// 17:35:00 PORTUGAL        
"%02d.%02d.%02d %s",				// 17.35.00 FINLAND         
"%02d:%02d:%02d %s",				// 17:35:00 ARABIC COUNTRIES
"%02d:%02d:%02d %s"				// 17:35:00 ISRAEL          
};

#define TIME_VALUES this->dt.tm_hour, this->dt.tm_min, this->dt.tm_sec, \
	    tz_table[this->tz_code]
#define MONTH_DATE_VALUES this->dt.tm_mon+1, this->dt.tm_mday, \
	    this->get_year (), tz_table[this->tz_code]
#define DATE_MONTH_VALUES this->dt.tm_mday, this->dt.tm_mon+1, \
	    this->get_year (), tz_table[this->tz_code]
#define YEAR_MONTH_VALUES this->get_year(), this->dt.tm_mday, \
            this->dt.tm_mon+1, tz_table[this->tz_code]

// adjust_tz -- Calculate adjustment for time zone from GMT in seconds
// Input:       None
// Output:      None

void Date_Time::adjust_tz () {
  long local_secs = 0;
  struct tm* t;					// Temporary variable
  long sys_seconds = time ((long*)0);		// System GMT time in seconds
  set_tz (this->tz_code);			// Setup local time conversion
  t = localtime (&sys_seconds);			// Convert to local time
  for (int i = 70; i < t->tm_year; i++) {
    local_secs += (YEAR + DAY);
    if (IS_LEAP_YEAR (i)) local_secs += DAY;
  }
  for (i = 0; i < t->tm_mon; i++) {
    local_secs += (DAY * days_in_month[i]);
    if (i == FEBRUARY && IS_LEAP_YEAR (t->tm_year))
      local_secs += DAY;
  }
  t->tm_mday--;
  local_secs += (DAY * t->tm_mday);
  local_secs += (HOUR * t->tm_hour);
  local_secs += (MINUTE * t->tm_min);
  local_secs += t->tm_sec;
  this->time_adjust = sys_seconds - local_secs; // TZ offset in seconds
}


// adjust_year -- Calculate offset from epoch to support years before 1/1/1970
// Input:         None
// Output:        None

void Date_Time::adjust_year (long& year) {

/**  
** The Date_Time class is  based upon the ANSI  C time(2) function. This
** calculates time/date in seconds since the  epoch (1/1/1970). To allow
** the Date_Time class to support dates before the epoch we need to find
** which year  after 1970 has the same  day of week/dates correlation as
** the one specified. To determine this, we make use of the  fact that a
** yearly periodic cycle of 28 years exists such that 1/1/1970 fell on a
** Thursday and 1/1/1998 is also a Thursday.
**/

  if (year > 99 && year < 1970) {
    this->year_adjust = int(1970 - year);
    year += NEW_YEAR (this->year_adjust);
  }
  else if (year < 70) {
    this->year_adjust = int(70 - year);
    year += NEW_YEAR (this->year_adjust);
  }
  else
    this->year_adjust = 0;
}

// set_tz -- Set the local time zone before conversion
//           of object from GMT seconds to tm structure
// Input:    Time zone
// Output:   None

void set_tz (time_zone tz) {
  char tz_name[50];				// Temporary variable
  strcpy (tz_name, "TZ=");			// TZ envionmental symbol
  strcpy (&tz_name[3], tz_table[tz]);		// Concatenate time zone name
  putenv (tz_name);				// Set environment TZ variable
  tzset();					// Reset time conversion codes
}


// resolve -- This private method takes a long representing the
//            number of seconds since 01/01/1970 00:00:00 GMT
//            and converts that into local time, placing the 
//            result into the date/time structure
// Input:     this*
// Output:    None -- date/time structure updated

void Date_Time::resolve () {
  struct tm* t;					// Temporary variable
  set_tz (this->tz_code);			// Set local time zone
  adjust_tz ();					// Calculate time zone offset
  t = localtime (&(this->time_seconds));	// Convert to local time zone
  this->dt.tm_sec = t->tm_sec;			// Copy seconds value
  this->dt.tm_min = t->tm_min;			// Copy minutes value
  this->dt.tm_hour = t->tm_hour;		// Copy hours value
  this->dt.tm_mday = t->tm_mday;		// Copy day of month value
  this->dt.tm_mon = t->tm_mon;			// Copy month value
  this->dt.tm_year = t->tm_year;		// Copy year value
  this->dt.tm_wday = t->tm_wday;		// Copy day of week value
  this->dt.tm_yday = t->tm_yday;		// Copy day of year value
  this->dt.tm_isdst = t->tm_isdst;		// Copy day light savings flag
}


// Date_Time() -- Simple constructor for empty Date_Time object
// Input:         None
// Output:        Date_Time reference

Date_Time::Date_Time () {
  this->time_seconds = time((long*)0);		// Zero seconds
#ifdef ERROR_CHECKING
  if (this->default_c_code_s == NULL)		// If no country code set
    RAISE (Warning, SYM(Date_Time), SYM(No_C_Code),
	   "Date_Time::Date_Time(): Default country code not set but used");
  if (this->default_tz_code_s == NULL)		// If no time zone code set
    RAISE (Warning, SYM(Date_Time), SYM(No_Tz_Code),
	   "Date_Time::Date_Time(): Default time zone code not set but used");
#endif
  this->c_code = this->default_c_code_s;	// Set country code
  this->tz_code = this->default_tz_code_s;	// Set time zone code
  set_tz (default_tz_code_s);			// Set local time zone
}


// Date_Time(tz,c) -- Simple constructor for empty Date_Time object
//                    that also sets the time zone and country
// Input:             Time zone, country codes
// Output:            Date_Time reference

Date_Time::Date_Time (time_zone tz, country c) {
  this->time_seconds = time((long*)0);		// Zero seconds
  this->c_code = c;				// Set country code
  this->tz_code = tz;				// Set time zone code
  set_tz (tz);					// Set local time zone
}


// Date_Time(const Date_Time&) -- Constructor to duplicate a Date_Time object
// Input:                        Date_Time reference
// Output:                       Date_Time reference

Date_Time::Date_Time (const Date_Time& d) {
  this->time_seconds = d.time_seconds;
  this->tz_code = d.tz_code;
  this->c_code = d.c_code;
  this->dt.tm_sec = d.dt.tm_sec;
  this->dt.tm_min = d.dt.tm_min;
  this->dt.tm_hour = d.dt.tm_hour;
  this->dt.tm_mday = d.dt.tm_mday;
  this->dt.tm_mon = d.dt.tm_mon;
  this->dt.tm_year = d.dt.tm_year;
  this->dt.tm_wday = d.dt.tm_wday;
  this->dt.tm_yday = d.dt.tm_yday;
  this->dt.tm_isdst = d.dt.tm_isdst;
  this->time_adjust = d.time_adjust;
  this->year_adjust = d.year_adjust;
  this->century = d.century;
}


// ~Date_Time -- Date_Time class destructor
// Input:        this*
// Output:       None

Date_Time::~Date_Time () { 
}
				       

// set_local_time -- Sets the time zone and DST adjusted local time
// Input:            this*
// Output:           Date_Time reference with slots updated for local time

void Date_Time::set_local_time () {
  struct tm* t;					// Temporary pointer variable
  long sys_seconds = time ((long*)0);		// System GMT time in seconds
  this->time_seconds = sys_seconds;		// Save time for future use
  set_tz (this->tz_code);			// Setup local time conversion
  t = localtime (&sys_seconds);			// Convert secs to local time
  this->dt.tm_sec = t->tm_sec;			// Copy seconds value
  this->dt.tm_min = t->tm_min;			// Copy minutes value
  this->dt.tm_hour = t->tm_hour;		// Copy hours value
  this->dt.tm_mday = t->tm_mday;		// Copy day of month value
  this->dt.tm_mon = t->tm_mon;			// Copy month value
  this->dt.tm_year = t->tm_year;		// Copy year value
  this->dt.tm_wday = t->tm_wday;		// Copy day of week value
  this->dt.tm_yday = t->tm_yday;		// Copy day of year value
  this->dt.tm_isdst = t->tm_isdst;		// Copy day light savings flag
  this->year_adjust = 0;
  this->century = 1900;
}


// set_gm_time -- Set the Greenwich Mean Time
// Input:         this*
// Output:        Date_Time reference with slots updated for GMT

void Date_Time::set_gm_time () {
  struct tm* t;					// Temporary pointer variable
  long sys_seconds = time ((long*)0);		// System GMT time in seconds
  this->tz_code = GB_EIRE;			// GMT time zone
  set_tz (this->tz_code);			// Setup GMT conversion
  t = localtime (&sys_seconds);			// Convert seconds to local time
  this->dt.tm_sec = t->tm_sec;			// Copy seconds value
  this->dt.tm_min = t->tm_min;			// Copy minutes value
  this->dt.tm_hour = t->tm_hour;		// Copy hours value
  this->dt.tm_mday = t->tm_mday;		// Copy day of month value
  this->dt.tm_mon = t->tm_mon;			// Copy month value
  this->dt.tm_year = t->tm_year;		// Copy year value
  this->dt.tm_wday = t->tm_wday;		// Copy day of week value
  this->dt.tm_yday = t->tm_yday;		// Copy day of year value
  this->dt.tm_isdst = t->tm_isdst;		// Copy day light savings flag
  this->year_adjust = 0;
  this->century = 1900;
}


// operator= -- Assignment operator to duplicate a Date_Time object
// Input:       Date_Time reference
// Output:      Date_Time reference

Date_Time& Date_Time::operator= (const Date_Time& d) {
  this->time_seconds = d.time_seconds;
  this->tz_code = d.tz_code;
  this->c_code = d.c_code;
  this->dt.tm_sec = d.dt.tm_sec;
  this->dt.tm_min = d.dt.tm_min;
  this->dt.tm_hour = d.dt.tm_hour;
  this->dt.tm_mday = d.dt.tm_mday;
  this->dt.tm_mon = d.dt.tm_mon;
  this->dt.tm_year = d.dt.tm_year;
  this->dt.tm_wday = d.dt.tm_wday;
  this->dt.tm_yday = d.dt.tm_yday;
  this->dt.tm_isdst = d.dt.tm_isdst;
  this->time_adjust = d.time_adjust;
  this->year_adjust = d.year_adjust;
  this->century = d.century;
  return *this;
}


// operator+= -- Add a time interval to a Date_Time object, assign
//               the result, and update the tm structure
// Input:        "n" seconds representing time interval
// Output:       Date_Time reference

Date_Time& Date_Time::operator+= (long n) {
  this->time_seconds += n;
  this->resolve ();
  return (*this);
}


// operator-= -- Subtract a time interval from a Date_Time object,
//               assign the result, and update the tm structure
// Input:        "n" seconds representing time interval
// Output:       Date_Time reference

Date_Time& Date_Time::operator-= (long n) {
  this->time_seconds -= n;
  this->resolve ();
  return (*this);
}


// operator>> -- Input Date_Time object from input stream. If a time zone is
//               also entered, it will be set too.
// Input:        Input stream reference, Date_Time reference
// Output:       Date_Time reference

istream& operator>> (istream& is, Date_Time& d) {
  char* s = "";					// Temporary pointer
  is.get(s,256);				// Read upto 256 characters
  d.parse(s,1);					// Parse input string
  return is;					// Return stream object
}


// incr_sec -- Advance Date/Time by "n" second(s)
// Input:      Number of seconds to advance (default 1)
// Output:     None -- Date_Time object updated 

void Date_Time::incr_sec (int n) {
  this->time_seconds += n;
  this->resolve ();
}


// incr_min -- Advance Date/Time by "n" minute(s)
// Input:      Number of minutes to advance (default 1)
// Output:     None -- Date_Time object updated 

void Date_Time::incr_min (int n) {
  this->time_seconds += (MINUTE * n);
  this->resolve ();
}


// incr_hour -- Advance Date/Time by "n" hour(s)
// Input:       Number of hours to advance (default 1)
// Output:      None -- Date_Time object updated 

void Date_Time::incr_hour (int n) {
  this->time_seconds += (HOUR * n);
  this->resolve ();
}


// incr_day -- Advance Date/Time by "n" day(s)
// Input:      Number of days to advance (default 1)
// Output:     None -- Date_Time object updated 

void Date_Time::incr_day (int n) {
  this->time_seconds += (DAY * n);
  this->resolve ();
}


// incr_week -- Advance Date/Time by "n" week(s)
// Input:       Number of weeks to advance (default 1)
// Output:      None -- Date_Time object updated 

void Date_Time::incr_week (int n) {
  this->time_seconds += (WEEK * n);
  this->resolve ();
}


// incr_month -- Advance Date/Time by "n" month(s)
// Input:        Number of months to advance (default 1)
// Output:       None -- Date_Time object updated

void Date_Time::incr_month (int n) {
  int temp_year = this->dt.tm_year;
  int temp_month = this->dt.tm_mon;
  for (int i = 0; i < n; i++, temp_month++) {
    if (temp_month > DECEMBER) {
      temp_month = JANUARY;
      temp_year++;
    }
    time_seconds += (DAY * days_in_month[temp_month]);
    if (temp_month == FEBRUARY && IS_LEAP_YEAR (temp_year))
      time_seconds += DAY;
  }
  this->resolve ();
}


// incr_year -- Advance Date/Time by "n" year(s)
// Input:       Number of years to advance (default 1)
// Output:      None -- Date_Time object updated 

void Date_Time::incr_year (int n) {
  int temp_year = this->dt.tm_year;
  for (int i = 0; i < n; i++, temp_year++) {
    time_seconds += (YEAR+DAY);
    if ((IS_LEAP_YEAR (temp_year) && (this->dt.tm_mon < FEBRUARY || 
	(this->dt.tm_mon == FEBRUARY && this->dt.tm_mday != 29))) ||
	(IS_LEAP_YEAR (temp_year+1) && this->dt.tm_mon > FEBRUARY))
      time_seconds += DAY;
  }
  this->resolve ();
}


// decr_month -- Retreat Date/Time by "n" month(s)
// Input:        Number of months to retreat (default 1)
// Output:       None -- Date_Time object updated

void Date_Time::decr_month (int n) {
  int temp_year = this->dt.tm_year;
  int temp_month = this->dt.tm_mon;
  for (int i = 0; i < n; i++, temp_month--) {
    if (temp_month < JANUARY) {
      temp_month = DECEMBER;
      temp_year--;
    }
    time_seconds -= (DAY * days_in_month[temp_month]);
    if (temp_month == FEBRUARY && IS_LEAP_YEAR (temp_year))
      time_seconds -= DAY;
  }
  this->resolve ();
}


// decr_year -- Retreat Date/Time by "n" year(s)
// Input:       Number of years to retreat (default 1)
// Output:      None -- Date_Time object updated 

void Date_Time::decr_year (int n) {
  int temp_year = this->dt.tm_year;
  for (int i = 0; i < n; i++, temp_year--) {
    time_seconds -= (YEAR+DAY);
    if ((IS_LEAP_YEAR (temp_year) && (this->dt.tm_mon > FEBRUARY ||
	(this->dt.tm_mon == FEBRUARY && this->dt.tm_mday == 29))) ||
	(IS_LEAP_YEAR (temp_year-1) && this->dt.tm_mon <= FEBRUARY))
      time_seconds -= DAY;
  }
  this->resolve ();
}


// start_min -- Increment Date_Time object to start of "n" minutes
// Input:       Number of minutes to advance to start (default 1)
//              where time is set to HH:MM:00
// Output:      None -- Date_Time object updated

void Date_Time::start_min (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec);
  incr_min (n-1);
}


// end_min -- Increment Date_Time object to end of "n" minutes
//            time is set to HH:MM:59
// Input:     Number of minutes to advance to end (default 1)
// Output:    None -- Date_Time object updated

void Date_Time::end_min (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec -1);
  incr_min (n-1);
}


// start_hour -- Increment Date_Time object to start of "n" hours
// Input:        Number of hours to advance to start (default 1)
//               where time is set to HH:00:00
// Output:       None -- Date_Time object updated

void Date_Time::start_hour (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  incr_hour (n-1);
}


// end_hour --   Increment Date_Time object to end of "n" hours
//               time is set to HH:59:59
// Input:        Number of hours to advance to end (default 1)
// Output:       None -- Date_Time object updated

void Date_Time::end_hour (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec - 1);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  incr_hour (n-1);
}


// start_day -- Increment Date_Time object to start of "n" days
//              time set to 00:00:00
// Input:       Number of days to advance to start (default 1)
// Output:      None -- Date_Time object updated

void Date_Time::start_day (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  incr_day (n-1);
}


// end_day -- Increment Date_Time object to end of "n" days
//            time set to 23:59:59
// Input:     Number of days to advance to end (default 1)
// Output:    None -- Date_Time object updated

void Date_Time::end_day (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec - 1);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  incr_day (n-1);
}


// start_week -- Increment Date_Time object to start of "n" weeks
//               Date/time set to Monday 00:00:00
// Input:        Number of weeks to advance to start (default 1)
// Output:       None -- Date_Time object updated

void Date_Time::start_week (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  this->time_seconds += (WEEK - (this->dt.tm_wday * DAY));
  this->time_seconds += DAY;
  incr_week (n-1);
}


// end_week -- Increment Date_Time object to end of "n" weeks
//             Date/Time set to Sunday 23:59:59
// Input:      Number of weeks to advance to end (default 1)
// Output:     None -- Date_Time object updated

void Date_Time::end_week (int n) {
  this->time_seconds += (MINUTE - this->dt.tm_sec - 1);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  this->time_seconds += (WEEK - (this->dt.tm_wday * DAY) - DAY);
  this->time_seconds += DAY;
  incr_week (n-1);
}


// start_month -- Increment Date_Time object to start of "n" months
//                Date/time set to 01/MM/YYYY 00:00:00
// Input:         Number of months to advance to start (default 1)
// Output:        None -- Date_Time object updated

void Date_Time::start_month (int n) {
  int temp_day = this->dt.tm_mday;		// Date in month
  int temp_month = this->dt.tm_mon;		// Current month
  this->time_seconds += (MINUTE - this->dt.tm_sec);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  for ( ; temp_day < days_in_month[temp_month]; temp_day++)
    this->time_seconds += DAY;
  if (temp_month == FEBRUARY && IS_LEAP_YEAR (this->dt.tm_year))
    this->time_seconds += DAY;
  incr_month (n-1);
}


// end_month -- Increment Date_Time object to end of "n" months
//              Date/time set to 31/MM/YYYY 23:59:59
// Input:       Number of months to advance to end (default 1)
// Output:      None -- Date_Time obejct updated

void Date_Time::end_month (int n) {
  int temp_day = this->dt.tm_mday;		// Date in month
  int temp_month = this->dt.tm_mon;		// Current month
  this->time_seconds += (MINUTE - this->dt.tm_sec - 1);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  for ( ; temp_day < days_in_month[temp_month]; temp_day++)
    this->time_seconds += DAY;
  if (temp_month == FEBRUARY && IS_LEAP_YEAR (this->dt.tm_year))
    this->time_seconds += DAY;
  incr_month (n-1);
}


// start_year -- Increment Date_Time object to start of "n" years
//               Date/Time set to 01/01/YYYY 00:00:00
// Input:        Number of years to advance to start (default 1)
// Output:       None -- Date_Time object updated

void Date_Time::start_year (int n) {
  int temp_year = this->dt.tm_year;
  int temp_day = this->dt.tm_mday;		// Date in month
  int temp_month = this->dt.tm_mon;		// Current month
  this->time_seconds += (MINUTE - this->dt.tm_sec);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  this->time_seconds += HOUR;
  for ( ; temp_day < days_in_month[temp_month]; temp_day++)
    this->time_seconds += DAY;
  if (temp_month == FEBRUARY && IS_LEAP_YEAR (temp_year))
    this->time_seconds += DAY;
  for (++temp_month; temp_month <= DECEMBER; temp_month++) {
    this->time_seconds += (DAY * days_in_month[temp_month]);
    if (temp_month == FEBRUARY && IS_LEAP_YEAR (temp_year))
      time_seconds += DAY;
  }
  incr_year (n-1);
}
  

// end_year -- Increment Date_Time object to end of "n" years
//             Date/time set to 31/12/YYYY 23:59:59
// Input:      Number of years to advance to end (default 1)
// Output:     None -- Date_Time object updated

void Date_Time::end_year (int n) {
  int temp_year = this->dt.tm_year;
  int temp_day = this->dt.tm_mday;		// Date in month
  int temp_month = this->dt.tm_mon;		// Current month
  this->time_seconds += (MINUTE - this->dt.tm_sec - 1);
  this->time_seconds += (HOUR - (this->dt.tm_min * MINUTE) - MINUTE);
  this->time_seconds += (DAY - (this->dt.tm_hour * HOUR) - HOUR);
  this->time_seconds += HOUR;
  for ( ; temp_day < days_in_month[temp_month]; temp_day++)
    this->time_seconds += DAY;
  if (temp_month == FEBRUARY && IS_LEAP_YEAR (temp_year))
    this->time_seconds += DAY;
  for (++temp_month; temp_month <= DECEMBER; temp_month++) {
    this->time_seconds += (DAY * days_in_month[temp_month]);
    if (temp_month == FEBRUARY && IS_LEAP_YEAR (temp_year))
      time_seconds += DAY;
  }
  incr_year (n-1);
}


// ascii_time -- Returns the time in ASCII format for the country
//               indicated in the Date_Time object. Note that the
//               storage is static and destroyed on the next call.
// Input:        this*
// Output:       Formatted char* string

const char* Date_Time::ascii_time () CONST {
  static char storage_s[35];
  switch (this->c_code) {

  case UNKNOWN_COUNTRY:
    sprintf (storage_s, time_format_s[UNKNOWN_COUNTRY]);
    break;

  case UNITED_STATES:
  case NORWAY:
  case GERMANY:
  case PORTUGAL:
  case FINLAND:
  case ARABIC_COUNTRIES:
  case ISRAEL:
  case LATIN_AMERICA:
  case NETHERLANDS:
  case BELGIUM:
  case FRANCE:
  case SPAIN:
  case ITALY:
  case SWITZERLAND:
  case DENMARK:
  case UNITED_KINGDOM:
  case SWEDEN:
  case FRENCH_CANADIAN:
    sprintf (storage_s, time_format_s[this->c_code], TIME_VALUES);
    break;
  }
  return storage_s;
}


// ascii_date -- Returns the date in ASCII format for the country
//               indicated in the Date_Time object. Note that the
//               storage is static and destroyed on the next call.
// Input:        this*
// Output:       Formatted char* string

const char* Date_Time::ascii_date () CONST {
  static char storage_s[11];
  switch (this->c_code) {

  case UNKNOWN_COUNTRY:
    sprintf (storage_s, date_format_s[UNKNOWN_COUNTRY]);
    break;

  case UNITED_STATES:
  case NORWAY:
  case GERMANY:
  case PORTUGAL:
  case FINLAND:
  case ARABIC_COUNTRIES:
  case ISRAEL:
  case LATIN_AMERICA:
  case NETHERLANDS:
  case BELGIUM:
  case FRANCE:
  case SPAIN:
  case ITALY:
  case SWITZERLAND:
  case DENMARK:
    sprintf (storage_s, date_format_s[this->c_code], MONTH_DATE_VALUES);
    break;

  case UNITED_KINGDOM:
    sprintf (storage_s, date_format_s[this->c_code], DATE_MONTH_VALUES);
    break;

  case SWEDEN:
  case FRENCH_CANADIAN:
    sprintf (storage_s, date_format_s[this->c_code], YEAR_MONTH_VALUES);
    break;
  }
  return storage_s;
}


// ascii_date_time -- Returns date and time in ASCII format for
//                    country indicated in Date_Time object. Note
//                    that the storage is static and destroyed 
//                    on the next call
// Input:             this*
// Output:            Formatted char* string

const char* Date_Time::ascii_date_time () CONST {
  static char storage_s[50];			// String to hold output
  switch (this->c_code) {

  case UNKNOWN_COUNTRY:
    sprintf (storage_s, date_format_s[UNKNOWN_COUNTRY]);

  case UNITED_STATES:
  case NORWAY:
  case GERMANY:
  case PORTUGAL:
  case FINLAND:
  case ARABIC_COUNTRIES:
  case ISRAEL:
  case LATIN_AMERICA:
  case NETHERLANDS:
  case BELGIUM:
  case FRANCE:
  case SPAIN:
  case ITALY:
  case SWITZERLAND:
  case DENMARK:
    sprintf (storage_s, date_format_s[this->c_code], MONTH_DATE_VALUES);
    storage_s[10] = ' ';
    sprintf (&storage_s[11], time_format_s[this->c_code], TIME_VALUES);
    break;

  case UNITED_KINGDOM:
    sprintf (storage_s, date_format_s[this->c_code], DATE_MONTH_VALUES);
    storage_s[10] = ' ';
    sprintf (&storage_s[11], time_format_s[this->c_code], TIME_VALUES);
    break;

  case SWEDEN:
  case FRENCH_CANADIAN:
    sprintf (storage_s, date_format_s[this->c_code], YEAR_MONTH_VALUES);
    storage_s[10] = ' ';
    sprintf (&storage_s[11], time_format_s[this->c_code], TIME_VALUES);
    break;
  }
  return storage_s;
}


// ascii_duration -- Returns the duration of time in an interval between this
//                   object and some other date/time object
// Input:            Reference to date/time object
// Output:           Formatted char* string

const char* Date_Time::ascii_duration (const Date_Time& d) CONST {
  static char storage_s[100];			// String to hold output
  long n = ABS((this->time_seconds-d.time_seconds)); // Difference in seconds
  long num_years = ABS((this->get_year() - d.get_year())); // Number years
  long num_weeks = (n % YEAR) / WEEK;		// Number of weeks
  long num_days = ((n % YEAR) % WEEK) / DAY;	// Number days
  long num_hours = (((n % YEAR) % WEEK) % DAY) / HOUR; // Number hours
  long num_minutes = ((((n % YEAR) % WEEK) % DAY) % HOUR) / MINUTE;
  long num_seconds = (((((n % YEAR) % WEEK) % DAY) % HOUR) % MINUTE);
  sprintf (storage_s,
     "%ld years, %ld weeks, %ld days, %ld hours, %ld minutes, %ld seconds",
      num_years, num_weeks, num_days, num_hours, num_minutes, num_seconds);
  return storage_s;
}


// inrange -- Takes a char and returns a 1 if it is a digit between 0 and 9,
//            returns 2 if it is a letter of the alphabet and returns 0
//            otherwise.  This is used by parse to break things into tokens.
//
// Input   -- a char
// Output  -- an int as described above.

int inrange (char c) {
  if (isdigit (c))				// If character is a digit
    return (1);					// Return indication
  if (isalpha (c))				// If character is a letter
    return(2);					// Return indication
  else return(0);				// Else return failure
}


// getzone -- This looks for a valid time zone in the char*, if one is
//            found the proper slots of the Date_Time object are set and
//            that part of the char* is deleted.
//
// Input   -- Takes a reference to a Date_Time object, and a char*
// Output  -- Does not return anything, just mutates the Date_Time object.
	
void getzone (Date_Time& d, char* s) {
  c_upcase(s);
  int ind = 0;
  long start,end;
  if (strfind(s,"*CANADA?EASTERN*",&start,&end)) {
    d.tz_code = CANADA_EASTERN;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*CANADA?CENTRAL*",&start,&end)) {
    d.tz_code = CANADA_CENTRAL;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*CANADA?MOUNTAIN*",&start,&end)) {
    d.tz_code = CANADA_MOUNTAIN;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*CANADA?PACIFIC*",&start,&end)) {
    d.tz_code = CANADA_PACIFIC;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*CANADA?YUKON*",&start,&end)) {
    d.tz_code = CANADA_YUKON;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*EASTERN*",&start,&end) || strfind(s,"*EDT*",&start,&end)) {
    d.tz_code = US_EASTERN;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*CENTRAL*",&start,&end) || strfind(s,"*CDT*",&start,&end)) {
    d.tz_code = US_CENTRAL;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*MOUNTAIN*",&start,&end) || strfind(s,"*MDT*",&start,&end)) {
    d.tz_code = US_MOUNTAIN;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*PACIFIC*",&start,&end)) {
    d.tz_code = US_PACIFIC;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*PACIFIC?NEW*",&start,&end)) {
    d.tz_code = US_PACIFIC_NEW;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*YUKON*",&start,&end)) {
    d.tz_code = US_YUKON;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*EAST?INDIANA*",&start,&end)) {
    d.tz_code = US_EAST_INDIANA;
    s = strnremove(s+start,end-start);
  }   
  else if (strfind(s,"*ARIZONA*",&start,&end)) {
    d.tz_code = US_ARIZONA;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*HAWAII*",&start,&end)) {
    d.tz_code = US_HAWAII;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*NEWFOUNDLAND*",&start,&end)) {
    d.tz_code = CANADA_NEWFOUNDLAND;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*ATLANTIC*",&start,&end)) {
    d.tz_code = CANADA_ATLANTIC;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*SASKATCHEWAN*",&start,&end)) {
    d.tz_code = CANADA_EAST_SASKATCHEWAN;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*EIRE*",&start,&end) || strfind(s,"*GMT*",&start,&end)) {
    d.tz_code = GB_EIRE;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*WET*",&start,&end)) {
    d.tz_code = WET;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*ICELAND*",&start,&end)) {
    d.tz_code = ICELAND;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*MET*",&start,&end)) {
    d.tz_code = MET;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*POLAND*",&start,&end)) {
   d.tz_code = POLAND;
   s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*EET*",&start,&end)) {
    d.tz_code = EET;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*TURKEY*",&start,&end)) {
    d.tz_code = TURKEY;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*W_SU*",&start,&end)) {
    d.tz_code = W_SU;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*PRC*",&start,&end)) {
    d.tz_code = PRC;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*KOREA*",&start,&end)) {
    d.tz_code = KOREA;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*JAPAN*",&start,&end)) {
   d.tz_code = JAPAN;
    s = strnremove(s+start,end-start);   
  }
  else if (strfind(s,"*SINGAPORE*",&start,&end)) {
    d.tz_code = SINGAPORE;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*HONGKONG*",&start,&end)) {
    d.tz_code = HONGKONG;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*ROC*",&start,&end)) {
    d.tz_code = ROC;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*TASMANIA*",&start,&end)) {
    d.tz_code = AUSTRALIA_TASMANIA;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*QUEENSLAND*",&start,&end)) {
    d.tz_code = AUSTRALIA_QUEENSLAND;
    s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*NORTH*",&start,&end)) {
   d.tz_code = AUSTRALIA_NORTH;
   s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*WEST*",&start,&end)) {
   d.tz_code = AUSTRALIA_WEST;
   s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*SOUTH*",&start,&end)) {
   d.tz_code = AUSTRALIA_SOUTH;
   s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*VICTORIA*",&start,&end)) {
   d.tz_code = AUSTRALIA_VICTORIA;
   s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*NSW*",&start,&end)) {
   d.tz_code = AUSTRALIA_NSW;
   s = strnremove(s+start,end-start);
  }
  else if (strfind(s,"*NZ*",&start,&end)) {
   d.tz_code = NZ;
   s = strnremove(s+start,end-start);
  }
}

// This is what a token looks like

struct token {
  int num, alph, used;
  String word;
};

// parse  -- This is the Date_Time parser, it is a member function.  It parses
//           the time and date out of the given char*, source, and sets all 
//           the appropriate slots of *this to the proper values.  If the 
//           optional argument settz is true, then the getzone function above
//           is called and searches for a timezone.
// Input  -- A char* source, and an optional int settz whose defalut is zero.
// Output -- Nothing is returned, the object is modified.

void Date_Time::parse (char* source, int settz) {
  if (settz) getzone(*this,source);
  #define TOKEN_LIMIT 16
  token token_list[TOKEN_LIMIT];
  int tcount = 0;
  token_list[0].alph = 0;
  token_list[0].num = 0;
  int len = strlen(source);
  int flag = 0;
  int ind;
  long year = -1;
  int month = -1;
  int day = -1;
  int hour = -1;
  int minute = -1;
  int second = -1;

  //////////////////////////////////////////////////////////////////////
  //
  // This part of the parser breaks source down into tokens.
  // 
  //////////////////////////////////////////////////////////////////////

  for (ind=0; ind<TOKEN_LIMIT; ind++)
    token_list[ind].used = 0;
  
  for (ind=0; ind < len; ind++)
    switch (inrange(source[ind])) {
    case 1:
      flag = 1;
      token_list[tcount].word += source[ind];
      token_list[tcount].num=1;
      break;
    case 2:
      flag = 1;
      token_list[tcount].word += source[ind];
      token_list[tcount].alph=1;
      break;
    case 0:
      if (flag) {
	if (++tcount > TOKEN_LIMIT)
	  RAISE (Error, SYM(Date_Time), SYM(Too_Many_Tokens),
		 "Date_Time::parse(): More than %d tokens in input",
		 TOKEN_LIMIT);
	token_list[tcount].alph = 0;
	token_list[tcount].num = 0;
      }
      flag = 0;
      break;
    }
  if (flag == 0) tcount--; 
  if (tcount <= 0)
    RAISE (Error, SYM(Date_Time), SYM(Too_Few_Tokens),
	   "Date_Time::parse(): Not enough tokens in input");

  //////////////////////////////////////////////////////////////////////
  //
  // This part of the parser checks for spelled out months, and converts
  // them to integer representations.
  // 
  //////////////////////////////////////////////////////////////////////
  
  char* m;
  for (ind=0; ind<=tcount; ind++)
    if (token_list[ind].num == 0 && token_list[ind].alph == 1) {
      m = c_upcase(token_list[ind].word);
      if (0 == strcmp(m,"JAN") || 0 == strcmp(m,"JANUARY")) {
	token_list[ind].word = "01";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"FEB") || 0 == strcmp(m,"FEBRUARY")) {
	token_list[ind].word = "02";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"MAR") || 0 == strcmp(m,"MARCH")) {
	token_list[ind].word = "03";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"APR") || 0 == strcmp(m,"APRIL")) {
	token_list[ind].word = "04";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"MAY")) {
	token_list[ind].word = "05";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"JUN") || 0 == strcmp(m,"JUNE")) {
	token_list[ind].word = "06";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"JUL") || 0 == strcmp(m,"JULY")) {
	token_list[ind].word = "07";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"AUG") || 0 == strcmp(m,"AUGUST")) {
	token_list[ind].word = "08";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"SEP") || 0 == strcmp(m,"SEPTEMBER")) {
	token_list[ind].word = "09";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"OCT") || 0 == strcmp(m,"OCTOBER")) {
	token_list[ind].word = "10";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"NOV") || 0 == strcmp(m,"NOVEMBER")) {
	token_list[ind].word = "11";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
      else if (0 == strcmp(m,"DEC") || 0 == strcmp(m,"DECEMBER")) {
	token_list[ind].word = "12";
	token_list[ind].num = 1;
	token_list[ind].alph = 0;
      }
    }

  ////////////////////////////////////////////////////////////////////////
  //
  // LOOKS FOR A YEAR, AND TAKES THE APPROPRIATE THINGS AROUND IT TO 
  // FILL IN THE DATE SLOTS (year, month, day).
  //
  ////////////////////////////////////////////////////////////////////////
  
  int lngth;
  flag = -1;
  for(ind=0; ind<=tcount; ind++)
    if (token_list[ind].num == 1 &&
	token_list[ind].alph == 0 &&
	(strlen(token_list[ind].word) > 2 ||
 	 atoi(token_list[ind].word) > 59)){
      flag = ind;
      break;
    }
  if (flag != -1) {
    year = atoi(token_list[flag].word);
    token_list[flag].used=1; 
    if (this->c_code==SWEDEN || this->c_code==FRENCH_CANADIAN) {
      for (++ind; token_list[ind].num == 1 &&
	   token_list[ind].alph != 1 &&
	   strlen(token_list[ind].word)<=2; ind++);
      ind = ind - flag -1;
      if (ind>2) ind=2;
      switch(ind){
      case 0:
	month = 1;
	day = 1;
      case 1:
	len = atoi(token_list[flag+1].word);
	if (len>31)
	  RAISE (Error, SYM(Date_Time), SYM(Unknown_Token),
		 "Date_Time::parse(): Unrecognized token in input");
	token_list[flag+1].used = 1;
	if (len>12) {
	  day = len;
	  month = 1;
	}
	else {
	  month = len;
	  day = 1;
	}
      case 2:
	len = atoi(token_list[flag+1].word);
	lngth = atoi(token_list[flag+2].word);
	if (len>31 || lngth>31)
	  RAISE (Error, SYM(Date_Time), SYM(Unknown_Token),
		 "Date_Time::parse(): Unrecognized token in input");
	token_list[flag+1].used = 1;
	if (len<13 && lngth>12) {
	  day = lngth;
	  month = len;
	  token_list[flag+2].used = 1;
	}
	else {
	  day = len;
	  if (lngth>12) month = 1;
	  else {
	    month=lngth;
	    token_list[flag+2].used = 1;
	  }
	}
      }
    }
    
    
    else {
      for (--ind; 0<=ind && token_list[ind].num == 1 &&
	   token_list[ind].alph != 1 &&
	   strlen(token_list[ind].word)<=2; ind--);
          
      ind = flag - ind - 1;
      if (ind>2) ind=2;
      switch(ind){
      case 0:
	month = 1;
	day = 1;
	break;
      case 1:
	len = atoi(token_list[flag-1].word);
	if (len>31)
	  RAISE (Error, SYM(Date_Time), SYM(Unknown_Token),
		 "Date_Time::parse(): Unrecognized token in input");
	token_list[flag-1].used = 1;
	if (len>12) {
	  day = len;
	  month = 1;
	}
	else {
	  month = len;
	  day = 1;
	}
	break;
      case 2:
	len = atoi(token_list[flag-1].word);
	lngth = atoi(token_list[flag-2].word);
	if (len>31 || lngth>31)
	  RAISE (Error, SYM(Date_Time), SYM(Unknown_Token),
		 "Date_Time::parse(): Unrecognized token in input");
	if (this->c_code!=UNITED_KINGDOM) {
	  token_list[flag-1].used = 1;
	  if (len<13 && lngth>12) {
	    day = lngth;
	    month = len;
	    token_list[flag-2].used = 1;
	  }
	  else {
	    day = len;
	    if (lngth>12) month = 1;
	    else {
	      month=lngth;
	      token_list[flag-2].used = 1;
	    }
	  }
	}
	else {
	  token_list[flag-2].used = 1;
	  if (lngth<13 && len>12) {
	    day = len;
	    month = lngth;
	    token_list[flag-1].used = 1;
	  }
	  else {
	    day = lngth;
	    if (len>12) month = 1;
	    else {
	      month=len;
	    token_list[flag-1].used = 1;
	    }
	  }
	}
	break;
      }
    }
  }
 
  ////////////////////////////////////////////////////////////////////////
  //
  // LOOKS FOR AN "AM" OR "PM" AND TAKES EVERY APROPRIATE TOKEN BEFORE 
  // THE "AM" OR "PM" TO FILL THE TIME SLOTS (hour, minute, second).
  //
  ////////////////////////////////////////////////////////////////////////

  int btrack;
  long start,end;
  for (ind=0; ind<=tcount; ind++) 
    if (token_list[ind].alph==1) {
      flag=0;
      c_upcase(token_list[ind].word);
      c_upcase(token_list[ind].word);
      if (strrfind(token_list[ind].word, "*PM*", &start, &end) ||
	  strrfind(token_list[ind].word, "*AM*", &start, &end) ) { 
	
	if (token_list[ind].word[start] == 'P') flag=1;    
	btrack = token_list[ind].num;
	len = (int) strlen(token_list[ind].word);
	
	if (btrack &&                          
	    (token_list[ind].word[len-1] != 'M' || len>4 || len<3))
	  RAISE (Error, SYM(Date_Time), SYM(Invalid_Time),
		 "Date_Time::parse(): Invalid time specified in input");
	if (btrack==0 && len != 2) 
	  RAISE (Error, SYM(Date_Time), SYM(Invalid_Time),
		 "Date_Time::parse(): Invalid time specified in input");
	for (int tmp = ind-1; 0<=tmp && token_list[tmp].num != 0 
	     && strlen(token_list[tmp].word)<=2
	     && token_list[tmp].used != 1; tmp--);
	tmp = ind - tmp - 1;
	if (btrack) tmp++;
	if (tmp>3) tmp = 3;
	// This case if part of the time is in the same token as the
	// "am" or "pm".

	if (btrack)    // PM/AM and number in one token
	  switch(tmp) {
	  case 1:
	    hour = atoi(token_list[ind].word);
	    if (hour>23)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Hour),
		     "Date_Time::parse(): Specified hour out of range");
	    if (flag && hour<12) hour += 12;
	    token_list[ind].used = 1;
	    minute = 0;
	    second = 0;
	    break;
	  case 2:
	    hour = atoi(token_list[ind-1].word);
	    if (hour>23)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Hour),
		     "Date_Time::parse(): Specified hour out of range");
	    if (flag && hour<12) hour += 12;
	    minute = atoi(token_list[ind].word);
	    if (minute>59)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Minutes),
		    "Date_Time::parse(): Specified minutes out of range");
	    token_list[ind].used = 1;
	    token_list[ind-1].used = 1;
	    second = 0;
	    break;
	  case 3:
	    hour = atoi(token_list[ind-2].word);
	    if (hour>23) 
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Hour),
		     "Date_Time::parse(): Specified hour out of range");
	    if (flag && hour<12) hour += 12;
	    minute = atoi(token_list[ind-1].word);
	    if (minute>59)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Minutes),
		     "Date_Time::parse(): Specified minutes out of range");
	    second = atoi(token_list[ind].word);
	    if (second>59)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Seconds),
		    "Date_Time::parse(): Specified seconds out of range");
	    token_list[ind].used = 1;
	    token_list[ind-1].used = 1;
	    token_list[ind-2].used = 1;
	    break;
	  }

	else
	
	  switch(tmp) {
	  case 0: 
	    minute = 0;
	    second = 0;
	    if (flag) hour=12; 
	    else hour=0; 
	    break;
	  case 1:
	    hour = atoi(token_list[ind-1].word);
	    token_list[ind-1].used = 1;
	    if (hour>23) 
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Hour),
		     "Date_Time::parse(): Specified hour out of range");
	    if (flag && hour<12) hour += 12;
	    minute = 0;
	    second = 0;
	    break;
	  case 2:
	    hour = atoi(token_list[ind-2].word);
	    if (hour>23)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Hour),
		     "Date_Time::parse(): Specified hour out of range");
	    if (flag && hour<12) hour += 12;
	    minute = atoi(token_list[ind-1].word);
	    if (minute>59)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Minutes),
		     "Date_Time::parse(): Specified minutes out of range");
	    token_list[ind-1].used = 1;
	    token_list[ind-2].used = 1;
	    second=0;
	    break;
	  case 3:
	    hour = atoi(token_list[ind-3].word);
	    if (hour>23)
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Hour),
		     "Date_Time::parse(): Specified hour out of range");
	    if (flag && hour<12) hour += 12;
	    minute = atoi(token_list[ind-2].word);
	    if (minute>59) 
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Minutes),
		     "Date_Time::parse(): Specified minutes out of range");
	    second = atoi(token_list[ind-1].word);
	    if (second>59) 
	      RAISE (Error, SYM(Date_Time), SYM(Invalid_Seconds),
		     "Date_Time::parse(): Specified seconds out of range");
	    token_list[ind-1].used = 1;
	    token_list[ind-2].used = 1;
	    token_list[ind-3].used = 1;
	    break;
	  }
	break;
      }

    }


  ////////////////////////////////////////////////////////////////////////
  //
  // This part of the parser fills in any remaining slots not filled 
  // above, and sets the rest to defaults.
  //
  ////////////////////////////////////////////////////////////////////////

  Date_Time current;
  current.set_local_time();
  int first = -1;
  int secnd = -1;

  if (year==-1) {
    int sind, find;
    for (ind=0; ind<= tcount;	 ind++) 
	   if (token_list[ind].num != 0 
	       && token_list[ind].alph == 0
	       && strlen(token_list[ind].word)<=2
	       && token_list[ind].used != 1) {
		 if (first != -1) {                    
		   secnd=atoi(token_list[ind].word);
		   sind=ind;
		   token_list[ind].used = 1;
		   break;
		 }
		 first=atoi(token_list[ind].word);
		 find = ind;
		 token_list[ind].used = 1;
	       }
  
    if (first!=-1 && first<=31){
      if (this->c_code != UNITED_KINGDOM) {
	if (secnd==-1 || secnd>31) day = first;
	else {
	  if (first>12 && secnd>12) {
	    day = first;
	    token_list[sind].used = 0;
	  }
	  else {
	    if (first <= 12){
	      month = first;
	      day = secnd;
	    }
	    else 
	      if (secnd <= 12) { 
		month = secnd;
		day = first;
	      }
	  }
	}
      }
      else {
	if (secnd==-1 || secnd>31) day = first;
	else {
	  if (first>12 && secnd>12) {
	    day = secnd;
	    token_list[find].used = 0;
	  }
	  else {
	    if (secnd <= 12){
	      month = secnd;
	      day = first;
	    }
	    else 
	      if (first <= 12) { 
		month = first;
		day = secnd;
	      }
	  }
	}
      }
    }
    
    if (year == -1) year = current.get_year();
    if (month == -1) month = current.get_mon() + 1;
    if (day == -1) day = current.get_mday();
  }


  // THIS PART DEALS WITH PARTS OF THE TIME NOT ALREADY FILLED IN

  if (hour == -1) {
    first = -1;
    secnd = -1;
    int third = -1;
    for (ind=0; ind<=tcount; ind++) 
      if (token_list[ind].num != 0 
	  && token_list[ind].alph == 0
	  && strlen(token_list[ind].word)<=2
	  && token_list[ind].used != 1) {
	  
	    if (secnd != -1) {
	      third=atoi(token_list[ind].word);
	      break;
	    }
	    if (first != -1)
	      secnd=atoi(token_list[ind].word);
	    else 
	      first=atoi(token_list[ind].word);
	  }
    

    if (third != -1) {
      second = third;
      if (second>59) 
	RAISE (Error, SYM(Date_Time), SYM(Invalid_Seconds),
	       "Date_Time::parse(): Specified seconds out of range");
    }
    if (secnd != -1) {
      minute = secnd;
      if (minute>59) 
	RAISE (Error, SYM(Date_Time), SYM(Invalid_Minutes),
	       "Date_Time::parse(): Specified minutes out of range");
    }
    if (first != -1) {
      hour = first;
      if (hour>23) 
	RAISE (Error, SYM(Date_Time), SYM(Invalid_Hour),
	       "Date_Time::parse(): Specified hour out of range");
    }
  }
  if (hour == -1) hour = 0;
  if (minute == -1) minute = 0;
  if (second == -1) second = 0;

  //////////////////////////////////////////////////////////////////////
  //
  // DO SOME FINAL BOUNDS CHECKING AND SET THE SLOTS OF OBJ 
  // TO THE CORRECT VALUES
  //
  //////////////////////////////////////////////////////////////////////

  this->adjust_year (year);
  month--;
  if (year > 99) {
    this->century = int((year / 100) * 100);
    year -= this->century;
  }
  else
    this->century = 1900;
  if ((month==0 || month==2 || month==4 || month==6 || month==7 || 
      month==9 || month==11) && day>31)
    RAISE (Error, SYM(Date_Time), SYM(Invalid_Day),
	   "Date_Time::parse(): Too many days for month of %s",
	  month_names[month]);
  else if ((month==3 || month==5 || month==8 || month==10) && day>30)
    RAISE (Error, SYM(Date_Time), SYM(Invalid_Day),
	   "Date_Time::parse(): Too many days for month of %s",
	   month_names[month]);
  else if (month==1 && day>28) 
    if (IS_LEAP_YEAR(year) && day < 30) ;
    else 
      RAISE (Error, SYM(Date_Time), SYM(Invalid_Day),
	     "Date_Time::parse(): Too many days for month of %s", 
	     month_names[month]);

   struct tm t;

   t.tm_sec = second;			// Copy seconds value
   t.tm_min = minute;			// Copy minutes value
   t.tm_hour = hour;			// Copy hours value
   t.tm_mday = day;			// Copy day of month value
   t.tm_mon = month;			// Copy month value
   t.tm_year = (int)year;		// Copy year value
   t.tm_isdst = 0;

   this->time_seconds = mktime (&t);

   if (this->time_seconds == -1)
     // Note: DOS returns -1 for any year prio to 1980
     this->time_seconds = time ((long*)0);
   
#if defined(DOS)
  // OS2 mktime increments the hour by one if daylight savings time is in effect
  // we have to roll everything back one to correct for this.
   adjust_dst(&t);
#endif

   this->dt.tm_sec = t.tm_sec;			// Copy seconds value
   this->dt.tm_min = t.tm_min;			// Copy minutes value
   this->dt.tm_hour = t.tm_hour;		// Copy hours value
   this->dt.tm_mday = t.tm_mday;		// Copy day of month value
   this->dt.tm_mon = t.tm_mon;			// Copy month value
   this->dt.tm_year = t.tm_year;		// Copy year value
   this->dt.tm_wday = t.tm_wday;		// Copy day of week value
   this->dt.tm_yday = t.tm_yday;		// Copy day of year value
   this->dt.tm_isdst = t.tm_isdst;		// Copy day light savings flag
}



// operator<< -- Output Date_Time object to output stream
// Input:        Output stream reference, Date_Time pointer
// Output:       Output stream reference

ostream& operator<< (ostream& os, const Date_Time* d) {
  return os << country_names[d->c_code] << " " << d->ascii_date_time ();
}


// operator<< -- Output Date_Time object to output stream
// Input:        Output stream reference, Date_Time reference
// Output:       Output stream reference

ostream& operator<< (ostream& os, const Date_Time& d) {
  return os << country_names[d.c_code] << " " << d.ascii_date_time ();
}
