//
// 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: LGO 07/03/89 -- Inherit from Generic
// Updated: MNF 07/25/89 -- Add the parse member function 
// Updated: MBN 09/06/89 -- Added conditional exception handling
// 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
//
// The Date_Time class provides  an   application programmer  with  simplified
// access to the operating system date and time values, along with support for
// comparison to other dates and times, calendar adjustment, and international
// support  for   display formats.   To  implement  this functionality,  three
// additional header files  are maintained: <country.h> providing symbolic and
// character    string  definitions  for      the various supported  contries;
// <timezone.h>  that enumerates symbolic  and character string values for all
// major  time zones; and <calendar.h>  that  lists  the days of the week  and
// months of the year.  The following countries and their associated date/time
// format are supported:
//
//                United States          03-29-1989 17:35:00
//                French Canadian        1989-29-03 17:35:00
//                Latin America          03/29/1989 17:35:00
//                Netherlands            03-29-1989 17:35:00
//                Belgium                03/29/1989 17:35:00
//                France                 03/29/1989 17:35:00
//                Spain                  03/29/1989 17:35:00
//                Italy                  03/29/1989 17:35:00
//                Switzerland            03.29.1989 17.35.00
//                United Kingdom         29-03-1989 17:35:00
//                Denmark                03/29/1989 17:35:00
//                Sweden                 1989-29-03 17.35.00
//                Norway                 03/29/1989 17:35:00
//                Germany                03.29.1989 17.35.00
//                Portugal               03/29/1989 17:35:00
//                Finland                03.29.1989 17.35.00
//                Arabic Countries       03/29/1989 17:35:00
//                Israel                 03 29 1989 17:35:00
//
// The private data section contains a  long integer that  maintains the system
// date and  time as an interval  from the base  date of 01/01/1970 00:00:00am.
// This  is supported  and  structure  in an ANSI  C <time.> tm structure that
// keeps separate fields for seconds,  minutes, hours,  day   of month, day  of
// week, month, year, and day of year.  Two other slots  provided time zone and
// country code values for   each  Date_Time object.   In addition,  two static
// slots containing  the default time zone and  country code for the  Date_Time
// class as a whole are defined.  The following time zones are supported:
//  
//        US/Eastern:                  Eastern time zone, USA                 
//        US/Central:                  Central time zone, USA                 
//        US/Mountain:                 Mountain time zone, USA                
//        US/Pacific:                  Pacific time zone, USA                 
//        US/Pacific-New:              Pacific time zone, USA with DST changes
//        US/Yukon:                    Yukon time zone, USA                   
//        US/East-Indiana:             Estern time zone, USA with no DST      
//        US/Arizona:                  Mountain time zone, USA with no DST    
//        US/Hawaii:                   Hawaii                                 
//        Canada/Newfoundland:         Newfoundland                           
//        Canada/Atlantic:             Atlantic time zone, Canada             
//        Canada/Eastern:              Eastern time zone, Canada              
//        Canada/Central:              Central time zone, Canada              
//        Canada/East-Saskatchewan:    Central time zone, Canada with no DST  
//        Canada/Mountain:             Mountain time zone, Canada             
//        Canada/Pacific:              Pacific time zone, Canada              
//        Canada/Yukon:                Yukon time zone, Canada                
//        GB-Eire:                     Great Britain and Eire (GMT)           
//        WET:                         Western Europe time                    
//        Iceland:                     Iceland                                
//        MET:                         Middle Europe time                     
//        Poland:                      Poland                                 
//        EET:                         Eastern Europe time                    
//        Turkey:                      Turkey                                 
//        W-SU:                        Western Soviet Union                   
//        PRC:                         People's Republic of China             
//        Korea:                       Republic of Korea                      
//        Japan:                       Japan                                  
//        Singapore:                   Singapore                              
//        Hongkong:                    Hongkong                               
//        ROC:                         Republic of China                      
//        Australia/Tasmania:          Tasmainia, Australia                   
//        Australia/Queensland:        Queensland, Australia                  
//        Australia/North:             Northern Territory, Australia          
//        Australia/West:              Western Australia                      
//        Australia/South:             South Australia                        
//        Australia/Victoria:          Victoria, Australia                    
//        Australia/NSW:               New South Wales, Australia             
//        NZ:                          New Zealand                            
//
// There are five constructors for the Date_Time  class.  The first constructor
// takes no arguments and intializes an empty object, setting the time zone and
// the  country  code   to  the  default values   for  the class.   The  second
// constructors takes a time  zone and country code and  initializes an  object
// for the specified local time zone and  country.  The third constructor takes
// a reference to some  other Date_Time object and  copies all the slot values.
// To get the time from a char*, first  create an  object with its country code
// set, and then use parse to fill in the slots.
//
// Public methods are provided for incrementing and  decrementing  the date and
// time, various   logical  tests   for  equality and  inequality,    and ASCII
// representation  of the   date  and/or  time   formatted   according  to  the
// appropriate country standard.  Two methods are  provided  to set a Date_Time
// object to either  the local time  (adjusted for  day  light savings time, if
// necessary) and  Greenwich Mean Time.   In  addition, several  public methods
// have been implemented for  interval arithmetic allowing messages  to be sent
// to the object along the lines of "move forward three weeks", "back up to end
// of last  month", etc.  Two  methods are  provided to  change the   time zone
// and/or country for a particular Date_Time object.  Finally accessors for all
// fields in the  <time.h> structure and  the  ASCII representation of the time
// zone and country are provided.
//
// Two other methods are also  implemented to  be used by  other methods of the
// class.  The first is a private member function that maintains  the integrity
// of   a  Date_Time object after  interval  arithmetic,  updating  to the next
// hour/day/month, etc.   The second is a  private friend function that takes a
// time zone value and  updates the local time  conversion information used for
// converting between GMT and the specified time zone.

#ifndef DATETIMEH				// If we have not defined class
#define DATETIMEH				// Indicate class Date_Time

#ifndef CHARH
#include <cool/char.h>
#endif

#ifndef STRINGH
#include <cool/String.h>
#endif

#ifndef COUNTRYH
#include <cool/country.h> 
#endif

#ifndef TIMEZONEH
#include <cool/timezone.h>
#endif

#ifndef CALENDARH
#include <cool/calendar.h>
#endif

#ifndef GENERIC_H
#include <cool/Generic.h>
#endif

#if defined(DOS)
extern "C" {
#include <time.h>
}
#else
#include <sys/time.h>
#endif

class Date_Time : public Generic {
  
private:
  long time_seconds;				// Seconds 01/01/1970 00:00:00
  struct tm dt;					// OS date/time structure
  country c_code;				// Retains date/time country
  time_zone tz_code;				// Retains time zone code
  long time_adjust;				// Time zone offset in seconds
  int year_adjust;				// Year offset prior to epoch
  int century;					// Maintains century of year

  static time_zone default_tz_code_s;		// Default time zone
  static country default_c_code_s;		// Default country
  void resolve ();				// Resolve/update object
  void adjust_tz ();				// Adjust for local time zone
  void adjust_year (long&);			// Adjust for years < 1/1/1970
  friend void set_tz (time_zone);		// Setup for local time zone
  
  friend int inrange (char);			// Helper function for parser
  friend void getzone (Date_Time&, char*);	// Gets time zone for parser

public:
  Date_Time();					// Simple constructor
  Date_Time(time_zone, country);		// Constructor with TZ/Country
  Date_Time(const Date_Time&);			// Constructor with reference
  ~Date_Time();                                  // Destructor
  
  void set_gm_time ();                          // Sets Greenwich Mean Time
  void set_local_time ();                       // Sets local time 
  inline void set_time_zone (time_zone);	// Set time zone for object
  inline void set_country (country);		// Set country for object
  inline friend void set_default_time_zone (time_zone);// Set default time zone
  inline friend void set_default_country (country);    // Set default country

  Date_Time& operator= (const Date_Time&);	// Date_Time x = y;
  inline long operator- (const Date_Time&);	// Interval subtraction
  Date_Time& operator+= (long);			// Interval addition/assignment
  Date_Time& operator-= (long);			// Interval subtraction/assign

  friend istream& operator>> (istream&, Date_Time&);
  friend ostream& operator<< (ostream&, const Date_Time*);
  friend ostream& operator<< (ostream&, const Date_Time&);
  
  void incr_sec (int n = 1);                    // Advance seconds (default 1)
  void incr_min (int n = 1);                    // Advance minutes (default 1)
  void incr_hour (int n = 1);                   // Advance hours (default 1)
  void incr_day (int n = 1);                    // Advance day (default 1)
  void incr_week (int n = 1);                   // Advance week (default 1)
  void incr_month (int n = 1);                  // Advance month (default 1)
  void incr_year (int n = 1);                   // Advance year (default 1)
  
  inline void decr_sec (int n = 1);             // Decrement second (default 1)
  inline void decr_min (int n = 1);             // Decrement minute (default 1)
  inline void decr_hour (int n = 1);            // Decrement hours (default 1)
  inline void decr_day (int n = 1);             // Decrement day (default 1)
  inline void decr_week (int n = 1);            // Decrement week (default 1)
  void decr_month (int n = 1);                  // Decrement month (default 1)
  void decr_year (int n = 1);                   // Decrement year (default 1)
  
  void start_min (int n = 1);   // Change to start of +/- "n" minute (default 1
  void end_min (int n = 1);     // Change to end of +/- "n" minutes (default 1)
  void start_hour (int n = 1);  // Change to start of +/- "n" hours (default 1)
  void end_hour (int n = 1);    // Change to end of +/- "n" hours (default 1)
  void start_day (int n = 1);   // Change to start of +/- "n" days (default 1)
  void end_day (int n = 1);     // Change to end of +/- "n" days (default 1)
  void start_week (int n = 1);  // Change to start of +/- "n" weeks (default 1)
  void end_week (int n = 1);    // Change to end of +/- "n" weeks (default 1)
  void start_month (int n = 1); // Change to start of +/- "n" month (default 1)
  void end_month (int n = 1);   // Change to end of +/- "n" months (default 1)
  void start_year (int n = 1);  // Change to start of +/- "n" years (default 1)
  void end_year (int n = 1);    // Change to end of +/- "n" years (default 1)

  inline Boolean operator== (const Date_Time&) CONST; // is same date/time
  inline Boolean operator!= (const Date_Time&) CONST; // is not same date/time
  inline Boolean operator< (const Date_Time&) CONST;  // is earlier date/time
  inline Boolean operator> (const Date_Time&) CONST;  // is later date/time
  inline Boolean operator<= (const Date_Time&) CONST; // is earlier or same
  inline Boolean operator>= (const Date_Time&) CONST; // is later or same

  const char* ascii_time () CONST;  // Return time in ASCII format for country
  const char* ascii_date () CONST;  // Return date in ASCII format for country
  const char* ascii_date_time () CONST; // Return date/time ASCII for country
  const char* ascii_duration (const Date_Time&) CONST; // Return time duration 
  void parse (char*, int settz = 0);	// Parses the char* into the Date_Time

  inline int get_sec () CONST;          // Return seconds from tm struct
  inline int get_min () CONST;		// Return minutes from tm struct
  inline int get_hour () CONST;		// Return hours from tm struct
  inline int get_mday () CONST;		// Return day of month from tm struct
  inline int get_mon () CONST;		// Return month from tm struct
  inline int get_year () CONST;		// Return year from tm struct
  inline int get_wday () CONST;		// Return day of week from tm struct
  inline int get_yday () CONST;		// Return day of year from tm struct
  inline Boolean is_day_light_savings () CONST; // Return daylight savings flag
  inline const char* get_time_zone () CONST;  // Return ASCII form of time zone
  inline const char* get_country () CONST;    // Return ASCII form of country 
};


// set_time_zone -- Set the local time zone of a Date_Time object
// Input:           Time zone
// Output:          None

inline void Date_Time::set_time_zone (time_zone tz) {
  this->tz_code = tz;
  this->resolve ();
}


// set_country -- Set the country of a Date_Time object
// Input:         Country code
// Output:        None

inline void Date_Time::set_country (country c) {
  this->c_code = c;
}


// set_default_time_zone -- Set the default time zone of the Date_Time class
// Input:           Time zone
// Output:          None

inline void set_default_time_zone (time_zone tz) {
  Date_Time::default_tz_code_s = tz;
}


// set_default_country -- Set the default country of the Date_Time class
// Input:         Country code
// Output:        None

inline void set_default_country (country c) {
  Date_Time::default_c_code_s = c;
}


// operator- -- Subtract one Date_Time from a another to calculate
//              the time interval between
// Input:       this*, Date_Time reference
// Output:      Number of seconds presenting time interval 

inline long Date_Time::operator- (const Date_Time& d) {
  return (this->time_seconds - d.time_seconds);
}


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

inline void Date_Time::decr_sec (int n) {
  incr_sec (-n);
}


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

inline void Date_Time::decr_min (int n) {
  incr_min (-n);
}


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

inline void Date_Time::decr_hour (int n) {
  incr_hour (-n);
}


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

inline void Date_Time::decr_day (int n) {
  incr_day (-n);
}


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

inline void Date_Time::decr_week (int n) {
  incr_week (-n);
}


// operator== -- Determine if two Date_Time objects represent the
//               same point in time
// Input:        this*, Date_Time reference
// Output:       TRUE/FALSE boolean value

inline Boolean Date_Time::operator== (const Date_Time& d) CONST {
  return ((this->time_seconds == d.time_seconds) ? TRUE : FALSE);
}


// operator!= -- Determine if two Date_Time objects represent 
//               different points in time
// Input:        this*, Date_Time reference
// Output:       TRUE/FALSE Boolean value

inline Boolean Date_Time::operator!= (const Date_Time& d) CONST {
  return ((this->time_seconds != d.time_seconds) ? TRUE : FALSE);
}


// operator< -- Determine if one Date_Time object comes before
//              some other point in time
// Input:       this*, Date_Time reference
// Output:      TRUE/FALSE Boolean value

inline Boolean Date_Time::operator< (const Date_Time& d) CONST {
  return ((this->time_seconds < d.time_seconds) ? TRUE : FALSE);
}


// operator> -- Determine if one Date_Time object comes aftern
//              some other point in time
// Input:       this*, Date_Time reference
// Output:      TRUE/FALSE Boolean value

inline Boolean Date_Time::operator> (const Date_Time& d) CONST {
  return ((this->time_seconds > d.time_seconds) ? TRUE : FALSE);
}


// operator<= -- Compare one Date_Time object to see if it lies before
//               or is the same as another Date_Time object
// Input:        this*, Date_Time reference
// Output:       TRUE/FALSE Boolean value

inline Boolean Date_Time::operator<= (const Date_Time& d) CONST {
  return ((this->time_seconds <= d.time_seconds) ? TRUE : FALSE);
}


// operator>= -- Compare one Date_Time object to see if it lies after
//               or is the same as another Date_Time object
// Input:        this*, Date_Time reference
// Output:       TRUE/FALSE boolean value

inline Boolean Date_Time::operator>= (const Date_Time& d) CONST {
  return ((this->time_seconds >= d.time_seconds) ? TRUE : FALSE);
}


// get_sec -- Accessor to seconds slot value
// Input:     this* 
// Output:    Integer representing number of seconds

inline int Date_Time::get_sec () CONST {
  return (this->dt.tm_sec);
}


// get_min -- Accessor to minutes slot value
// Input:     this* 
// Output:    Integer representing number of minutes

inline int Date_Time::get_min () CONST {
  return (this->dt.tm_min);
}


// get_hour -- Accessor to hour slot value
// Input:      this* 
// Output:     Integer representing number of hour

inline int Date_Time::get_hour () CONST {
  return (this->dt.tm_hour);
}


// get_mday -- Accessor to day of month slot value
// Input:      this* 
// Output:     Integer representing day of month

inline int Date_Time::get_mday () CONST {
  return (this->dt.tm_mday);
}


// get_mon -- Accessor to month slot value
// Input:     this* 
// Output:    Integer representing month

inline int Date_Time::get_mon () CONST {
  return (this->dt.tm_mon);
}

#define YEAR_PERIOD 28
#define NEW_YEAR(x) (((x / YEAR_PERIOD) * YEAR_PERIOD) + YEAR_PERIOD)
#define OLD_YEAR(x,y) (x - NEW_YEAR (y))

// get_year -- Accessor to year slot value
// Input:      this* 
// Output:     Integer representing year

inline int Date_Time::get_year () CONST {
  return ((this->year_adjust > 0) ? (this->century+OLD_YEAR (this->dt.tm_year,
					       this->year_adjust)) :
	  this->century + this->dt.tm_year);
}


// get_yday -- Accessor to day of year slot value
// Input:      this* 
// Output:     Integer representing day of year

inline int Date_Time::get_yday () CONST {
  return (this->dt.tm_yday);
}


// get_wday -- Accessor to day of week slot value
// Input:      this* 
// Output:     Integer representing day of week

inline int Date_Time::get_wday () CONST {
  return (this->dt.tm_wday);
}


// is_day_light_savings -- Accessor to Day Light Savings time flag
// Input:                  this* 
// Output:                 TRUE/FALSE if in effect

inline Boolean Date_Time::is_day_light_savings () CONST {
  return ((this->dt.tm_isdst) ? TRUE : FALSE);
}


// get_time_zone -- Accessor to ASCII representation of time zone
// Input:           this* 
// Output:          character string representing time zone

inline const char* Date_Time::get_time_zone () CONST {
  return (tz_table[this->tz_code]);
}


// get_country -- Accessor to country code slot value
// Input:         this* 
// Output:        character string representing country

inline const char* Date_Time::get_country () CONST {
  return (country_names[this->c_code]);
}

#endif                                          // End of DATETIMEH

