//==================================================================================================
// C a l e n d a r                                                                        Interface
// G r e g o r i a n
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides facilities to manipulate date and time information in the Gregorian
   calendar. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "calendar/gregorian.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guCalendarGregorian
#define guCalendarGregorian

// Headers //---------------------------------------------------------------------------------------
#include <bpp/standard.hpp> /*INCLUDE*/

namespace bpp {

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef CALENDAR_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Namespaces //------------------------------------------------------------------------------------
#define public_area  calendarGregorian
#define private_area calendarGregorian_private

namespace public_area  { using namespace standard; }
namespace private_area { using namespace public_area; }

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniCalendarGregorian
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------------------Classes
 class clGregorian;
 //-----------------------------------------------------------------------------------Variable Types
 /*TYPE*/ /* Symbolic constants for the months. */
 enumeration { january=1, february=2, march=3, april=4, may=5, june=6,
               july=7, august=8, september=9, october=10, november=11, december=12 } tyMonth;

 /*TYPE*/ /* Symbolic constants for the week days. */
 enumeration { sunday=1, monday=2, tuesday=3, wednesday=4, thursday=5, friday=6,
               saturday=7 } tyWeekDay;

 /*TYPE*/ /* Symbolic constants for the kind of year. */
 enumeration { normalYear=false, leapYear=true } tyYearKind;

 /*TYPE*/ /* Symbolic constants for the string format of the date. */
 enumeration { dayFirstLongDate, dayFirstShortDate,
               monthFirstLongDate, monthFirstShortDate } tyDateFormat;

 /*TYPE*/ /* Symbolic constants for the string format of the time. */
 enumeration { americanLongTime, americanShortTime, militaryLongTime,
               militaryShortTime } tyTimeFormat;
 //-----------------------------------------------------------------------------------Constant Types
 typedef const clGregorian ctGregorian;

 typedef const tyMonth    tcMonth;
 typedef const tyWeekDay  tcWeekDay;
 typedef const tyYearKind tcYearKind;

 typedef const tyDateFormat tcDateFormat;
 typedef const tyTimeFormat tcTimeFormat;
}

namespace private_area {}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area  {
 inline tyInteger monthLength(tyMonth,tyYearKind=normalYear);
 inline tcString  monthName(tyMonth);
 inline tyInteger nbDaySinceNewYear(tyMonth,tyYearKind);
 inline tcString  weekDayName(tyWeekDay);

 inline tyBoolean operator == (ctGregorian &,ctGregorian &);
 inline tyBoolean operator < (ctGregorian &,ctGregorian &);
 inline tyBoolean operator != (ctGregorian &,ctGregorian &);
 inline tyBoolean operator > (ctGregorian &,ctGregorian &);
 inline tyBoolean operator <= (ctGregorian &,ctGregorian &);
 inline tyBoolean operator >= (ctGregorian &,ctGregorian &);

 inline clOutStream &  operator << (clOutStream &,ctGregorian &);
 function clInStream & operator >> (clInStream &,clGregorian &);

 function clGregorian current(void);
}

namespace private_area {
 testing_mode (
  function void autoTest(void);
  function void test(void);
 )
}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 /*ERROR*/ extern_error erInvalidAbsoluteDay; /* Invalid absolute day number. */
 /*ERROR*/ extern_error erInvalidDateString; /* Invalid date string. */
 /*ERROR*/ extern_error erInvalidHour; /* Invalid hour number. */
 /*ERROR*/ extern_error erInvalidHundredth; /* Invalid hundredth number. */
 /*ERROR*/ extern_error erInvalidMinute; /* Invalid minute number. */
 /*ERROR*/ extern_error erInvalidMonth; /* Invalid month number. */
 /*ERROR*/ extern_error erInvalidMonthDay; /* Invalid month day number. */
 /*ERROR*/ extern_error erInvalidSecond; /* Invalid second number. */
 /*ERROR*/ extern_error erInvalidTimeString; /* Invalid time string. */
 /*ERROR*/ extern_error erInvalidWeekDay; /* Invalid week day number. */
 /*ERROR*/ extern_error erInvalidYear; /* Invalid year number. */

 /*ERROR*/ extern_error erTimeZoneParameterMissing;
 /* The <CODE>time_zone</CODE> parameter is missing in the initialization file. */
}

// Constants & Variables //-------------------------------------------------------------------------
/*CONSTANT*/ /* Week day of the 1/1/1. */
extern_static_constant(public,tyWeekDay,goFirstWeekDay,firstWeekDay);

/*CONSTANT*/ /* Current time zone. */
extern_static_constant(public,tyInteger,goTimeZone,timeZone);

/*CONSTANT*/ /* String indicating the current time zone. */
extern_static_constant(public,tyString,goTimeZoneString,timeZoneString);

/*CONSTANT*/ /* Number of hours per day. */
extern_static_constant(public,tyInteger,goHourPerDay,hourPerDay);

/*CONSTANT*/ /* Number of minutes per day. */
extern_static_constant(public,tyInteger,goMinutePerDay,minutePerDay);

/*CONSTANT*/ /* Number of seconds per day. */
extern_static_constant(public,tyInteger,goSecondPerDay,secondPerDay);

/*CONSTANT*/ /* Number of hundredths per second. */
extern_static_constant(public,tyInteger,goHundredthPerSecond,hundredthPerSecond);

/*CONSTANT*/ /* Number of hundredths per minute. */
extern_static_constant(public,tyInteger,goHundredthPerMinute,hundredthPerMinute);

/*CONSTANT*/ /* Number of hundredths per hour. */
extern_static_constant(public,tyInteger,goHundredthPerHour,hundredthPerHour);

/*CONSTANT*/ /* Number of hundredths per day. */
extern_static_constant(public,tyInteger,goHundredthPerDay,hundredthPerDay);

extern_dynamic_constant(private,clString,goDataLocation,?);

extern_static_constant(private,tyInteger,goMonthLengthS[13][2],?);
extern_static_constant(private,tcString,goMonthNameS[13],?);
extern_static_constant(private,tyInteger,goNbDaySinceNewYearS[13][2],?);
extern_static_constant(private,tcString,goWeekDayNameS[8],?);

// G r e g o r i a n  Interface //------------------------------------------------------------------
namespace public_area {
 /*CLASS clGregorian */ /* Represents a date-and-time in the Gregorian calendar. */
 class clGregorian {
  //-----------------------------------------------------------------------------------------Private
  private_property void computeAbsoluteDay(void);
  private_property void computeAbsoluteHundredth(void);

  private_property void computeDate(void);
  private_property void computeTime(void);

  private_property void update(tyInteger,tyInteger);
  //------------------------------------------------------------------------------------------Public
  /*ATTRIBUTE clGregorian */ /* Absolute day number (from 1 to +oo). */
  read_only_attribute(tyInteger,atAbsoluteDay,absoluteDay);

  /*ATTRIBUTE clGregorian */ /* Absolute hundreth number in the day (from 0 to 8,639,999). */
  read_only_attribute(tyInteger,atAbsoluteHundredth,absoluteHundredth);

  /*ATTRIBUTE clGregorian */ /* Year number (from 1 to +oo). */
  read_only_attribute(tyInteger,atYear,year);

  /*ATTRIBUTE clGregorian */ /* Month of the year (from 1 to 12). */
  read_only_attribute(tyMonth,atMonth,month);

  /*ATTRIBUTE clGregorian */ /* Day of the month (from 1 to 31). */
  read_only_attribute(tyInteger,atMonthDay,monthDay);

  /*ATTRIBUTE clGregorian */ /* Hour of the day (from 0 to 23). */
  read_only_attribute(tyInteger,atHour,hour);

  /*ATTRIBUTE clGregorian */ /* Minute of the hour (from 0 to 59). */
  read_only_attribute(tyInteger,atMinute,minute);

  /*ATTRIBUTE clGregorian */ /* Second of the minute (from 0 to 59). */
  read_only_attribute(tyInteger,atSecond,second);

  /*ATTRIBUTE clGregorian */ /* Hundredth of the second (from 0 to 99). */
  read_only_attribute(tyInteger,atHundredth,hundredth);

  public_property tyInteger yearDay(void) const;
  public_property tyWeekDay weekDay(void) const;

  public_property tyYearKind yearKind(void) const;

  public_property constructor clGregorian(tyInteger=1,tyMonth=january,tyInteger=1,
                                          tyInteger=0,tyInteger=0,tyInteger=0,tyInteger=0);

  public_property constructor clGregorian(ctGregorian &);
  public_property destructor  clGregorian(void);

  public_property clGregorian & operator = (ctGregorian &);

  public_property clString date(tyDateFormat=monthFirstShortDate,tyBoolean=true) const;
  public_property clString time(tyTimeFormat=militaryShortTime) const;

  public_property clString dateAndTime(tyDateFormat=monthFirstShortDate,
                                       tyTimeFormat=militaryShortTime,tyBoolean=true) const;

  public_property void addYears(tyInteger);
  public_property void addMonths(tyInteger);
  public_property void addDays(tyInteger);
  public_property void addHours(tyInteger);
  public_property void addMinutes(tyInteger);
  public_property void addSeconds(tyInteger);
  public_property void addHundredths(tyInteger);

  public_property void setYear(tyInteger);
  public_property void setMonth(tyInteger);
  public_property void setYearDay(tyInteger);
  public_property void setMonthDay(tyInteger);
  public_property void setWeekDay(tyInteger);
  public_property void setHour(tyInteger);
  public_property void setMinute(tyInteger);
  public_property void setSecond(tyInteger);
  public_property void setHundredth(tyInteger);

  public_property void setDate(tyInteger=1,tyMonth=january,tyInteger=1);
  public_property void setDate(tcString);
  public_property void setTime(tyInteger=0,tyInteger=0,tyInteger=0,tyInteger=0);
  public_property void setTime(tcString);
 };
}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------MonthLength
 /*FUNCTION*/ /* Returns the number of days in a month. */
 inline tyInteger monthLength(tyMonth agMonth,tyYearKind agYearKind) {
  if (not (agMonth>=1 and agMonth<=12)) send_inline_error(erInvalidMonth,"monthLength");
  return (private_area::goMonthLengthS[agMonth][agYearKind]);
 }
 //----------------------------------------------------------------------------------------MonthName
 /*FUNCTION*/ /* Returns the name of a month. */
 inline tcString monthName(tyMonth agMonth) {
  if (not (agMonth>=1 and agMonth<=12)) send_inline_error(erInvalidMonth,"monthName");
  return (private_area::goMonthNameS[agMonth]);
 }
 //--------------------------------------------------------------------------------NbDaySinceNewYear
 /*FUNCTION*/
 /* Returns the number of days elapsed from the 1st of January to the 1st of a given month,
    according to a kind of year (leap or not). */
 inline tyInteger nbDaySinceNewYear(tyMonth agMonth,tyYearKind agYearKind) {
  if (not (agMonth>=1 and agMonth<=12)) send_inline_error(erInvalidMonth,"nbDaysSinceNewYear");
  return (private_area::goNbDaySinceNewYearS[agMonth][agYearKind]);
 }
 //--------------------------------------------------------------------------------------WeekDayName
 /*FUNCTION*/ /* Returns the name of a week day. */
 inline tcString weekDayName(tyWeekDay agWeekDay) {
  if (not (agWeekDay>=1 and agWeekDay<=7)) send_inline_error(erInvalidWeekDay,"weekDayName");
  return (private_area::goWeekDayNameS[agWeekDay]);
 }
 //--------------------------------------------------------------------------------------Operator ==
 /*FUNCTION*/ /* Checks if two calendars are equal. */
 inline tyBoolean operator == (ctGregorian & agCalendar1,ctGregorian & agCalendar2) {
  return (agCalendar1.absoluteDay()==agCalendar2.absoluteDay() and
          agCalendar1.absoluteHundredth()==agCalendar2.absoluteHundredth());
 }
 //---------------------------------------------------------------------------------------Operator <
 /*FUNCTION*/ /* Checks if a calendar is before another one. */
 inline tyBoolean operator < (ctGregorian & agCalendar1,ctGregorian & agCalendar2) {
  return (agCalendar1.absoluteDay()<agCalendar2.absoluteDay() or
          (agCalendar1.absoluteDay()==agCalendar2.absoluteDay() and
           agCalendar1.absoluteHundredth()<agCalendar2.absoluteHundredth()));
 }
 //--------------------------------------------------------------------------------------Operator !=
 /*FUNCTION*/ /* Checks if two calendars are different. */
 inline tyBoolean operator != (ctGregorian & agCalendar1,ctGregorian & agCalendar2)
 { return (not (agCalendar1==agCalendar2)); }
 //---------------------------------------------------------------------------------------Operator >
 /*FUNCTION*/ /* Checks if a calendar is after another one. */
 inline tyBoolean operator > (ctGregorian & agCalendar1,ctGregorian & agCalendar2)
 { return (agCalendar2<agCalendar1); }
 //--------------------------------------------------------------------------------------Operator <=
 /*FUNCTION*/ /* Checks if a calendar is before or equal to another one. */
 inline tyBoolean operator <= (ctGregorian & agCalendar1,ctGregorian & agCalendar2)
 { return (not (agCalendar2<agCalendar1)); }
 //--------------------------------------------------------------------------------------Operator >=
 /*FUNCTION*/ /* Checks if a calendar is after or equal to another one. */
 inline tyBoolean operator >= (ctGregorian & agCalendar1,ctGregorian & agCalendar2)
 { return (not (agCalendar1<agCalendar2)); }
 //--------------------------------------------------------------------------------------Operator <<
 /*FUNCTION*/ /* Writes a calendar into a stream. */
 inline clOutStream & operator << (clOutStream & agStream,ctGregorian & agCalendar) {
  agStream << agCalendar.dateAndTime(monthFirstShortDate,militaryLongTime,false);
  if (agStream.fail()) send_inline_error(erStreamWriting,"gregorian::operator <<");
  return (agStream);
 }
}

namespace private_area {}

// C a l e n d a r  Inline //-----------------------------------------------------------------------
namespace public_area {
 //-----------------------------------------------------------------------------------------YearKind
 /*METHOD clGregorian */ /* Returns the kind of the year (leap or not). */
 inline tyYearKind clGregorian::yearKind(void) const {
  if (atYear%400==0) return (leapYear);
  if (atYear%4==0 and not (atYear%100==0)) return (leapYear);
  return (normalYear);
 }
 //-------------------------------------------------------------------------------ComputeAbsoluteDay
 inline void clGregorian::computeAbsoluteDay(void) {
  tyInteger lcPreviousYear = atYear-1;

  atAbsoluteDay = atMonthDay + nbDaySinceNewYear(atMonth,yearKind()) + lcPreviousYear*365
                  + lcPreviousYear/4 - lcPreviousYear/100 + lcPreviousYear/400;
 }
 //-------------------------------------------------------------------------ComputeAbsoluteHundredth
 inline void clGregorian::computeAbsoluteHundredth(void)
 { atAbsoluteHundredth = ((atHour*60 + atMinute)*60 + atSecond)*100 + atHundredth; }
 //-------------------------------------------------------------------------------------------Update
 inline void clGregorian::update(tyInteger agDays,tyInteger agHundredths) {
  tyInteger lcOldDay = atAbsoluteDay;

  atAbsoluteDay += agDays + agHundredths/hundredthPerDay() - (agHundredths<0);
  if (atAbsoluteDay<1) send_inline_error(erInvalidAbsoluteDay,"gregorian::update");
  if (atAbsoluteDay!=lcOldDay) computeDate();

  atAbsoluteHundredth=(hundredthPerDay()+agHundredths)%hundredthPerDay();
  computeTime();
 }
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clGregorian */ /* Destructs the calendar. */
 inline clGregorian::~clGregorian(void) {}
 //------------------------------------------------------------------------------------------YearDay
 /*METHOD clGregorian */ /* Returns the day of the year (from 1 to 366). */
 inline tyInteger clGregorian::yearDay(void) const {
  tyYearKind lcYearKind = yearKind();

  return (nbDaySinceNewYear(atMonth,lcYearKind) + atMonthDay);
 }
 //------------------------------------------------------------------------------------------WeekDay
 /*METHOD clGregorian */ /* Returns the day of the week (from 1 = Sunday to 7 = Saturday). */
 inline tyWeekDay clGregorian::weekDay(void) const
 { return (tyWeekDay((atAbsoluteDay+private_area::firstWeekDay()-2)%7 + 1)); }
 //------------------------------------------------------------------------------------------SetYear
 /*METHOD clGregorian */ /* Sets the year. */
 inline void clGregorian::setYear(tyInteger agNumber) {
  if (agNumber<1) send_inline_error(erInvalidYear,"gregorian::setYear");
  addYears(agNumber-atYear);
 }
 //-----------------------------------------------------------------------------------------SetMonth
 /*METHOD clGregorian */ /* Sets the month. */
 inline void clGregorian::setMonth(tyInteger agNumber) {
  if (agNumber<1 or agNumber>12) send_inline_error(erInvalidMonth,"gregorian::setMonth");
  addMonths(agNumber-atMonth);
 }
 //---------------------------------------------------------------------------------------SetYearDay
 /*METHOD clGregorian */ /* Sets the day of the year. */
 inline void clGregorian::setYearDay(tyInteger agNumber) {
  if (agNumber<1 or agNumber>(yearKind()==leapYear ? 366 : 365))
   send_inline_error(erInvalidYear,"gregorian::setYearDay");

  addDays(agNumber-yearDay());
 }
 //--------------------------------------------------------------------------------------SetMonthDay
 /*METHOD clGregorian */ /* Sets the day of the month. */
 inline void clGregorian::setMonthDay(tyInteger agNumber) {
  if (agNumber<1 or agNumber>monthLength(atMonth,yearKind()))
   send_inline_error(erInvalidMonthDay,"gregorian::setMonthDay");

  addDays(agNumber-atMonthDay);
 }
 //---------------------------------------------------------------------------------------SetWeekDay
 /*METHOD clGregorian */ /* Sets the day of the week. */
 inline void clGregorian::setWeekDay(tyInteger agNumber) {
  if (agNumber<1 or agNumber>7) send_inline_error(erInvalidWeekDay,"gregorian::setWeekDay");
  addDays(agNumber-weekDay());
 }
 //------------------------------------------------------------------------------------------SetHour
 /*METHOD clGregorian */ /* Sets the hour. */
 inline void clGregorian::setHour(tyInteger agNumber) {
  if (agNumber<0 or agNumber>23) send_inline_error(erInvalidHour,"gregorian::setHour");
  atAbsoluteHundredth+=(agNumber-atHour)*hundredthPerHour();
  atHour=agNumber;
 }
 //----------------------------------------------------------------------------------------SetMinute
 /*METHOD clGregorian */ /* Sets the minute. */
 inline void clGregorian::setMinute(tyInteger agNumber) {
  if (agNumber<0 or agNumber>59) send_inline_error(erInvalidMinute,"gregorian::setMinute");
  atAbsoluteHundredth+=(agNumber-atMinute)*hundredthPerMinute();
  atMinute=agNumber;
 }
 //----------------------------------------------------------------------------------------SetSecond
 /*METHOD clGregorian */ /* Sets the second. */
 inline void clGregorian::setSecond(tyInteger agNumber) {
  if (agNumber<0 or agNumber>59) send_inline_error(erInvalidSecond,"gregorian::setSecond");
  atAbsoluteHundredth+=(agNumber-atSecond)*hundredthPerSecond();
  atSecond=agNumber;
 }
 //-------------------------------------------------------------------------------------SetHundredth
 /*METHOD clGregorian */ /* Sets the hundredth. */
 inline void clGregorian::setHundredth(tyInteger agNumber) {
  if (agNumber<0 or agNumber>99) send_inline_error(erInvalidHundredth,"gregorian::setHundredth");
  atAbsoluteHundredth+=(agNumber-atHundredth);
  atHundredth=agNumber;
 }
}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// C a l e n d a r                                                                   Implementation
// G r e g o r i a n
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "calendar/gregorian.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define CALENDAR_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/calendar/gregorian.hpp> /*INTERFACE*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  calendarGregorian
#define private_area calendarGregorian_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Calendar/Gregorian");

// Initialization //--------------------------------------------------------------------------------
#undef iniCalendarGregorian
static_constant(private_area::clInitializer,goInitializer);

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 static_error erInvalidAbsoluteDay;
 static_error erInvalidDateString;
 static_error erInvalidHour;
 static_error erInvalidHundredth;
 static_error erInvalidMinute;
 static_error erInvalidMonth;
 static_error erInvalidMonthDay;
 static_error erInvalidSecond;
 static_error erInvalidTimeString;
 static_error erInvalidWeekDay;
 static_error erInvalidYear;
 static_error erTimeZoneParameterMissing;
}

// Constants & Variables //-------------------------------------------------------------------------
static_constant(tyWeekDay,goFirstWeekDay);
static_constant(tyInteger,goTimeZone);
static_constant(tyString,goTimeZoneString);

static_constant(tyInteger,goHourPerDay);
static_constant(tyInteger,goMinutePerDay);
static_constant(tyInteger,goSecondPerDay);

static_constant(tyInteger,goHundredthPerSecond);
static_constant(tyInteger,goHundredthPerMinute);
static_constant(tyInteger,goHundredthPerHour);
static_constant(tyInteger,goHundredthPerDay);

dynamic_constant(clString,goDataLocation);

static_constant(tyInteger,goMonthLengthS[13][2]);
static_constant(tcString,goMonthNameS[13]);
static_constant(tyInteger,goNbDaySinceNewYearS[13][2]);
static_constant(tcString,goWeekDayNameS[8]);

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Operator >>
 /*FUNCTION*/ /* Reads a calendar from a stream. */
 function clInStream & operator >> (clInStream & agStream,clGregorian & agCalendar) {
  method_name("gregorian::operator >>");

  clString lcString;

  agStream >> lcString;
  agCalendar.setDate(lcString.data());
  agStream >> lcString;
  agCalendar.setTime(lcString.data());

  if (agStream.fail()) send_error(erStreamReading);
  return (agStream);
 }
 //------------------------------------------------------------------------------------------Current
 /*FUNCTION*/ /* Returns the current date-and-time. */
 function clGregorian current(void) {
  clGregorian lcCurrent(1970,january,1,0,0,0,0);

  lcCurrent.addSeconds(time(nil));
  lcCurrent.addHours(timeZone());
  return (lcCurrent);
 }
}

namespace private_area {}

// G r e g o r i a n  Implementation //-------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------ComputeDate
 property void clGregorian::computeDate(void) {
  tyInteger  lcTempoDay;
  tyInteger  lcYearDay;
  tyYearKind lcYearKind;

  // Year and day of the year //
  atYear=tyInteger(tyReal(atAbsoluteDay)/365.2425);
  lcYearDay=atAbsoluteDay-(365*atYear + atYear/4 - atYear/100 + atYear/400);

  if (lcYearDay>365) {
   atYear++;
   lcTempoDay=atAbsoluteDay-(365*atYear + atYear/4 - atYear/100 + atYear/400);

   if (lcTempoDay>0) {
    lcYearDay=lcTempoDay;
    atYear++;
   }
  }
  else {
   if (lcYearDay<1) {
    atYear--;
    lcYearDay=atAbsoluteDay-(365*atYear + atYear/4 - atYear/100 + atYear/400);
   }

   atYear++;
  }

  // Month and day of the month //
  lcYearKind=yearKind();
  atMonth=tyMonth(lcYearDay/32 + 1);

  atMonthDay=lcYearDay-nbDaySinceNewYear(atMonth,lcYearKind);

  if (atMonthDay>monthLength(atMonth,lcYearKind)) {
   atMonthDay-=monthLength(atMonth,lcYearKind);
   nextSymbol(atMonth);
  }
 }
 //--------------------------------------------------------------------------------------ComputeTime
 property void clGregorian::computeTime(void) {
  tyInteger lcNbHundredth = atAbsoluteHundredth;

  atHour=lcNbHundredth/hundredthPerHour();
  lcNbHundredth%=hundredthPerHour();
  atMinute=lcNbHundredth/hundredthPerMinute();
  lcNbHundredth%=hundredthPerMinute();
  atSecond=lcNbHundredth/hundredthPerSecond();
  atHundredth=lcNbHundredth%hundredthPerSecond();
 }
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clGregorian */ /* Builds a calendar from separated information. */
 property clGregorian::clGregorian(tyInteger agYear,tyMonth agMonth,tyInteger agMonthDay,
                          tyInteger agHour,tyInteger agMinute,tyInteger agSecond,
                          tyInteger agHundredth)
 : atYear(),atMonth(january),atMonthDay(),atHour(),atMinute(),atSecond(),atHundredth() {
  setDate(agYear,agMonth,agMonthDay);
  setTime(agHour,agMinute,agSecond,agHundredth);
 }
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clGregorian */ /* Builds a calendar from another one. */
 property clGregorian::clGregorian(ctGregorian & agCalendar) :
  atAbsoluteDay(agCalendar.atAbsoluteDay),atAbsoluteHundredth(agCalendar.atAbsoluteHundredth),
  atYear(agCalendar.atYear),atMonth(agCalendar.atMonth),atMonthDay(agCalendar.atMonthDay),
  atHour(agCalendar.atHour),atMinute(agCalendar.atMinute),atSecond(agCalendar.atSecond),
  atHundredth(agCalendar.atHundredth) {}
 //---------------------------------------------------------------------------------------Operator =
 /*METHOD clGregorian */ /* Copies a calendar. */
 property clGregorian & clGregorian::operator = (ctGregorian & agCalendar) {
  atAbsoluteDay=agCalendar.atAbsoluteDay;
  atAbsoluteHundredth=agCalendar.atAbsoluteHundredth;

  atYear=agCalendar.atYear;
  atMonth=agCalendar.atMonth;
  atMonthDay=agCalendar.atMonthDay;

  atHour=agCalendar.atHour;
  atMinute=agCalendar.atMinute;
  atSecond=agCalendar.atSecond;
  atHundredth=agCalendar.atHundredth;

  return (*this);
 }
 //-----------------------------------------------------------------------------------------AddYears
 /*METHOD clGregorian */ /* Add years to the calendar. */
 property void clGregorian::addYears(tyInteger agNumber) {
  method_name("gregorian::addYears");

  atYear+=agNumber;
  if (atYear<1) send_error(erInvalidYear);
  computeAbsoluteDay();
 }
 //----------------------------------------------------------------------------------------AddMonths
 /*METHOD clGregorian */ /* Add months to the calendar. */
 property void clGregorian::addMonths(tyInteger agNumber) {
  method_name("gregorian::addMonths");

  tyInteger lcMonth = tyInteger(atMonth)-1+agNumber%12;

  atYear+=agNumber/12+lcMonth/12-(lcMonth<0);
  atMonth=tyMonth((12+lcMonth)%12 + 1);
  if (atYear<1) send_error(erInvalidYear);
  computeAbsoluteDay();
 }
 //------------------------------------------------------------------------------------------AddDays
 /*METHOD clGregorian */ /* Add days to the calendar. */
 property void clGregorian::addDays(tyInteger agNumber) {
  method_name("gregorian::addDays");

  atAbsoluteDay+=agNumber;
  if (atAbsoluteDay<1) send_error(erInvalidAbsoluteDay);
  computeDate();
 }
 //-----------------------------------------------------------------------------------------AddHours
 /*METHOD clGregorian */ /* Add hours to the calendar. */
 property void clGregorian::addHours(tyInteger agNumber) {
  tyInteger lcDays = agNumber/hourPerDay();

  update(lcDays,atAbsoluteHundredth+(agNumber-lcDays*hourPerDay())*hundredthPerHour());
 }
 //---------------------------------------------------------------------------------------AddMinutes
 /*METHOD clGregorian */ /* Add minutes to the calendar. */
 property void clGregorian::addMinutes(tyInteger agNumber) {
  tyInteger lcDays = agNumber/minutePerDay();

  update(lcDays,atAbsoluteHundredth+(agNumber-lcDays*minutePerDay())*hundredthPerMinute());
 }
 //---------------------------------------------------------------------------------------AddSeconds
 /*METHOD clGregorian */ /* Add seconds to the calendar. */
 property void clGregorian::addSeconds(tyInteger agNumber) {
  tyInteger lcDays = agNumber/secondPerDay();

  update(lcDays,atAbsoluteHundredth+(agNumber-lcDays*secondPerDay())*hundredthPerSecond());
 }
 //------------------------------------------------------------------------------------AddHundredths
 /*METHOD clGregorian */ /* Add hundredths to the calendar. */
 property void clGregorian::addHundredths(tyInteger agNumber) {
  tyInteger lcDays = agNumber/hundredthPerDay();

  update(lcDays,atAbsoluteHundredth+(agNumber-lcDays*hundredthPerDay()));
 }
 //------------------------------------------------------------------------------------------SetDate
 /*METHOD clGregorian */ /* Sets the date from separated information. */
 property void clGregorian::setDate(tyInteger agYear,tyMonth agMonth,tyInteger agMonthDay) {
  method_name("gregorian::setDate");

  if (agYear<1) send_error(erInvalidYear);
  atYear=agYear;

  if (agMonth<1 or agMonth>12) send_error(erInvalidMonth);
  atMonth=agMonth;

  if (agMonthDay<1 or agMonthDay>monthLength(agMonth,yearKind())) send_error(erInvalidMonthDay);
  atMonthDay=agMonthDay;

  computeAbsoluteDay();
 }
 //------------------------------------------------------------------------------------------SetDate
 /*METHOD clGregorian */ /* Sets the date from a string. */
 property void clGregorian::setDate(tcString agString) {
  method_name("gregorian::setDate");

  tyString lcString = new_array(tyCharacter,size(agString)+1);
  tcString lcBuffer = lcString;

  tyInteger lcMonth;
  tyInteger lcMonthDay;
  tyInteger lcYear;

  copy(lcString,agString);
  replace(lcString,'/',' ');
  lcBuffer=bpp::low::string::toInteger(lcBuffer,lcMonth);
  lcBuffer=bpp::low::string::toInteger(lcBuffer,lcMonthDay);
  lcBuffer=bpp::low::string::toInteger(lcBuffer,lcYear);

  delete_array(lcString);
  if (lcBuffer==nil) send_error(erInvalidDateString);
  setDate(lcYear,tyMonth(lcMonth),lcMonthDay);
 }
 //------------------------------------------------------------------------------------------SetTime
 /*METHOD clGregorian */ /* Sets the time from separated information. */
 property void clGregorian::setTime(tyInteger agHour,tyInteger agMinute,tyInteger agSecond,
                                    tyInteger agHundredth) {
  method_name("gregorian::setTime");

  if (agHour<0 or agHour>23) send_error(erInvalidHour);
  if (agMinute<0 or agMinute>59) send_error(erInvalidMinute);
  if (agSecond<0 or agSecond>59) send_error(erInvalidSecond);
  if (agHundredth<0 or agHundredth>99) send_error(erInvalidHundredth);

  atHour=agHour;
  atMinute=agMinute;
  atSecond=agSecond;
  atHundredth=agHundredth;
  computeAbsoluteHundredth();
 }
 //------------------------------------------------------------------------------------------SetTime
 /*METHOD clGregorian */ /* Sets the time from a string. */
 property void clGregorian::setTime(tcString agString) {
  method_name("gregorian::setTime");

  tyString lcString = new_array(tyCharacter,size(agString)+1);
  tcString lcBuffer = lcString;

  tyInteger lcHour;
  tyInteger lcHundredth;
  tyInteger lcMinute;
  tyInteger lcSecond;

  copy(lcString,agString);
  replace(lcString,':',' ');
  lcBuffer=bpp::low::string::toInteger(lcBuffer,lcHour);
  lcBuffer=bpp::low::string::toInteger(lcBuffer,lcMinute);
  lcBuffer=bpp::low::string::toInteger(lcBuffer,lcSecond);
  lcBuffer=bpp::low::string::toInteger(lcBuffer,lcHundredth);

  delete_array(lcString);
  if (lcBuffer==nil) send_error(erInvalidTimeString);
  setTime(lcHour,lcMinute,lcSecond,lcHundredth);
 }
 //---------------------------------------------------------------------------------------------Date
 /*METHOD clGregorian */ /* Returns a string representing the date in a given format. */
 property clString clGregorian::date(tyDateFormat agFormat,tyBoolean agWeekDay) const {
  tyCharacter lcBuffer[64];
  tyCharacter lcString[80];

  switch (agFormat) {
   case dayFirstLongDate:
    if (agWeekDay) {
     bpp::low::string::copy(lcString,weekDayName(weekDay()));
     bpp::low::string::concatenate(lcString,", ");
    }
    else bpp::low::string::copy(lcString,"");

    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMonthDay));
    bpp::low::string::concatenate(lcString," ");
    bpp::low::string::concatenate(lcString,monthName(atMonth));
    bpp::low::string::concatenate(lcString," ");
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atYear));

    break;

   case dayFirstShortDate:
    if (agWeekDay) {
     bpp::low::string::copy(lcString,weekDayName(weekDay()));
     bpp::low::string::concatenate(lcString,", ");
    }
    else bpp::low::string::copy(lcString,"");

    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMonthDay));
    bpp::low::string::concatenate(lcString,"/");

    bpp::low::string::concatenate(lcString,
                                  bpp::low::string::toString(lcBuffer,tyInteger(atMonth)));

    bpp::low::string::concatenate(lcString,"/");
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atYear));

    break;

   case monthFirstLongDate:
    if (agWeekDay) {
     bpp::low::string::copy(lcString,weekDayName(weekDay()));
     bpp::low::string::concatenate(lcString,", ");
    }
    else bpp::low::string::copy(lcString,"");

    bpp::low::string::concatenate(lcString,monthName(atMonth));
    bpp::low::string::concatenate(lcString," ");
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMonthDay));
    bpp::low::string::concatenate(lcString,", ");
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atYear));

    break;

   default:
    if (agWeekDay) {
     bpp::low::string::copy(lcString,weekDayName(weekDay()));
     bpp::low::string::concatenate(lcString,", ");
    }
    else bpp::low::string::copy(lcString,"");

    bpp::low::string::concatenate(lcString,
                                  bpp::low::string::toString(lcBuffer,tyInteger(atMonth)));

    bpp::low::string::concatenate(lcString,"/");
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMonthDay));
    bpp::low::string::concatenate(lcString,"/");
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atYear));
  }

  return (clString(lcString));
 }
 //---------------------------------------------------------------------------------------------Time
 /*METHOD clGregorian */ /* Returns a string representing the time in a given format. */
 property clString clGregorian::time(tyTimeFormat agFormat) const {
  tyCharacter lcBuffer[64];
  tyCharacter lcString[20];

  tyInteger lcHour;

  switch (agFormat) {
   case americanLongTime:
    lcHour=atHour%12;
    if (lcHour==0) lcHour=12;

    bpp::low::string::copy(lcString,(lcHour<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,lcHour));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atMinute<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMinute));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atSecond<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atSecond));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atHundredth<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atHundredth));
    bpp::low::string::concatenate(lcString," ");
    bpp::low::string::concatenate(lcString,(atHour<12 ? "AM" : "PM"));

    break;

   case americanShortTime:
    lcHour=atHour%12;
    if (lcHour==0) lcHour=12;

    bpp::low::string::copy(lcString,(lcHour<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,lcHour));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atMinute<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMinute));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atSecond<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atSecond));
    bpp::low::string::concatenate(lcString," ");
    bpp::low::string::concatenate(lcString,(atHour<12 ? "AM" : "PM"));

    break;

   case militaryLongTime:
    bpp::low::string::copy(lcString,(atHour<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atHour));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atMinute<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMinute));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atSecond<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atSecond));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atHundredth<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atHundredth));

    break;

   default:
    bpp::low::string::copy(lcString,(atHour<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atHour));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atMinute<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atMinute));
    bpp::low::string::concatenate(lcString,":");
    bpp::low::string::concatenate(lcString,(atSecond<10 ? "0" : ""));
    bpp::low::string::concatenate(lcString,bpp::low::string::toString(lcBuffer,atSecond));
  }

  return (clString(lcString));
 }
 //--------------------------------------------------------------------------------------DateAndTime
 /*METHOD clGregorian */ /* Returns a string representing the date-and-time in a given format. */
 property clString clGregorian::dateAndTime(tyDateFormat agDateFormat,tyTimeFormat agTimeFormat,
                                            tyBoolean agWeekDay) const {
  tyCharacter lcString[100];

  bpp::low::string::copy(lcString,date(agDateFormat,agWeekDay).data());
  bpp::low::string::concatenate(lcString,", ");
  bpp::low::string::concatenate(lcString,time(agTimeFormat).data());

  return (clString(lcString));
 }
}

namespace private_area {}

// I n i t i a l i z e r  Implementation //---------------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------------Start
 property void clInitializer::start(void) {
  tyCardinal lcMonth;

  if (atCounter++ == 0) {
   try {
    #include <bpp/modules.hpp> /*NEED*/
    registerStop(this);
    environment::informInitialization(goModuleName);

    method_name("initializer::constructor");

    tyCharacter lcBuffer[64];

    erInvalidAbsoluteDay.create("Gregorian Calendar - Invalid absolute day number.");
    erInvalidDateString.create("Gregorian Calendar - Invalid date string.");
    erInvalidHour.create("Gregorian Calendar - Invalid hour number.");
    erInvalidHundredth.create("Gregorian Calendar - Invalid hundredth number.");
    erInvalidMinute.create("Gregorian Calendar - Invalid minute number.");
    erInvalidMonth.create("Gregorian Calendar - Invalid month number.");
    erInvalidMonthDay.create("Gregorian Calendar - Invalid month day number.");
    erInvalidSecond.create("Gregorian Calendar - Invalid second number.");
    erInvalidTimeString.create("Gregorian Calendar - Invalid time string.");
    erInvalidWeekDay.create("Gregorian Calendar - Invalid week day number.");
    erInvalidYear.create("Gregorian Calendar - Invalid year number.");
    erTimeZoneParameterMissing.create("The 'time_zone' parameter is missing.");

    if (environment::parameters().count(clString("time_zone"))==0)
     send_error(erTimeZoneParameterMissing);

    goFirstWeekDay   = monday;
    goTimeZone       = integer(environment::parameter(clString("time_zone")).data());
    goTimeZoneString = new_array(tyCharacter,20);

    if (goTimeZone==0) bpp::low::string::copy(goTimeZoneString,"GMT");
    else if (goTimeZone<0) {
     bpp::low::string::copy(goTimeZoneString,"GMT ");

     bpp::low::string::concatenate(goTimeZoneString,
                                   bpp::low::string::toString(lcBuffer,goTimeZone));
    }
    else {
     bpp::low::string::copy(goTimeZoneString,"GMT +");

     bpp::low::string::concatenate(goTimeZoneString,
                                   bpp::low::string::toString(lcBuffer,goTimeZone));
    }

    goHourPerDay   = 24;
    goMinutePerDay = goHourPerDay*60;
    goSecondPerDay = goMinutePerDay*60;

    goHundredthPerSecond = 100;
    goHundredthPerMinute = goHundredthPerSecond*60;
    goHundredthPerHour   = goHundredthPerMinute*60;
    goHundredthPerDay    = goHundredthPerHour*24;

    goDataLocation = new_object(clString(environment::dataLocation()+fileNameSeparator()+"calendar"
                     +fileNameSeparator()+"gregorian"));

    goMonthLengthS[january][normalYear]   = 31;
    goMonthLengthS[february][normalYear]  = 28;
    goMonthLengthS[march][normalYear]     = 31;
    goMonthLengthS[april][normalYear]     = 30;
    goMonthLengthS[may][normalYear]       = 31;
    goMonthLengthS[june][normalYear]      = 30;
    goMonthLengthS[july][normalYear]      = 31;
    goMonthLengthS[august][normalYear]    = 31;
    goMonthLengthS[september][normalYear] = 30;
    goMonthLengthS[october][normalYear]   = 31;
    goMonthLengthS[november][normalYear]  = 30;
    goMonthLengthS[december][normalYear]  = 31;

    goMonthLengthS[january][leapYear]     = 31;
    goMonthLengthS[february][leapYear]    = 29;
    goMonthLengthS[march][leapYear]       = 31;
    goMonthLengthS[april][leapYear]       = 30;
    goMonthLengthS[may][leapYear]         = 31;
    goMonthLengthS[june][leapYear]        = 30;
    goMonthLengthS[july][leapYear]        = 31;
    goMonthLengthS[august][leapYear]      = 31;
    goMonthLengthS[september][leapYear]   = 30;
    goMonthLengthS[october][leapYear]     = 31;
    goMonthLengthS[november][leapYear]    = 30;
    goMonthLengthS[december][leapYear]    = 31;

    goMonthNameS[january]   = "January";
    goMonthNameS[february]  = "February";
    goMonthNameS[march]     = "March";
    goMonthNameS[april]     = "April";
    goMonthNameS[may]       = "May";
    goMonthNameS[june]      = "June";
    goMonthNameS[july]      = "July";
    goMonthNameS[august]    = "August";
    goMonthNameS[september] = "September";
    goMonthNameS[october]   = "October";
    goMonthNameS[november]  = "November";
    goMonthNameS[december]  = "December";

    lcMonth=january;
    goNbDaySinceNewYearS[january][normalYear]=0;
    goNbDaySinceNewYearS[january][leapYear]=0;

    while (lcMonth<december) {
     goNbDaySinceNewYearS[lcMonth+1][normalYear]=goNbDaySinceNewYearS[lcMonth][normalYear] +
                                                 goMonthLengthS[lcMonth][normalYear];

     goNbDaySinceNewYearS[lcMonth+1][leapYear]=goNbDaySinceNewYearS[lcMonth][leapYear] +
                                               goMonthLengthS[lcMonth][leapYear];
     lcMonth++;
    }

    goWeekDayNameS[sunday]    = "Sunday";
    goWeekDayNameS[monday]    = "Monday";
    goWeekDayNameS[tuesday]   = "Tuesday";
    goWeekDayNameS[wednesday] = "Wednesday";
    goWeekDayNameS[thursday]  = "Thursday";
    goWeekDayNameS[friday]    = "Friday";
    goWeekDayNameS[saturday]  = "Saturday";

    bpp::low::string::copy(environment_private::goStartingDate,current().dateAndTime().data());
    environment_private::goTerminationDate[0]=end_string;
   }

   initializer_catch;
  }
 }
 //---------------------------------------------------------------------------------------------Stop
 property void clInitializer::stop(void) {
  try {
   environment::informTermination(goModuleName);

   bpp::low::string::copy(environment_private::goTerminationDate,current().dateAndTime().data());

   delete_array(goTimeZoneString);
   delete_object(goDataLocation);
  }

  initializer_catch;
 }
}

// End //-------------------------------------------------------------------------------------------
}