/*------------------------------------------------------------
   Date and time stored procedures
--------------------------------------------------------------

   Written by:
     Gábor Gazsó

     Address: HUNGARY H-1590 Budapest, PO. Box 113.
     Phone:   +36 30 411-790
     e-mail:  intelli@mail.matav.hu

--------------------------------------------------------------
This script file contains the following procedures:

  Year         - Returns the year part of a date.
  Month        - Returns the month part of a date.
  Day          - Returns the day part of a date.
  EncodeDate   - Creates a date from its parts.
  DecodeDate   - Extracts a date to its parts.
  ElapsedDays  - Returns the number of days between two days.
  Modulus      - Returns the modulus of a division.
  DayOfWeek    - Returns the number of the day of week.
  Time         - Returns the hour and minute of a date.
  DaysOfMonth  - Returns the number of days in a given month.
  DayOfYear    - Returns the number of the day in the given year.
  AddMonth     - Adds given number of months to a date.
--------------------------------------------------------------
*/
   
SET TERM # ;

/* The calculation is based on the following mathematical algorithm:
  ElapsedDays = ElapsedYears*365 +(ElapsedYears/4) +(ElapsedYears/100) +(ElapsedYears/400)
  ElapsedDays = ElapsedYears*365 +(ElapsedYears*97/400)
  ElapsedDays = ElapsedYears*146097/400
  ElapsedDays*400/146097 = ElapsedYears
*/
CREATE PROCEDURE Year(ADate DATE)
RETURNS (Result INTEGER)
AS
DECLARE VARIABLE ElapsedDays  INTEGER;
DECLARE VARIABLE ElapsedYears INTEGER;
BEGIN
  /* Lets go to the begin of the year */
  WHILE(CAST(ADate AS VARCHAR(5)) <> "1-JAN") DO
    ADate = ADate -1;
    
  ElapsedDays = ADate -"1.1.1901";
  Result = (ElapsedDays *400 /146097)+1901;
END
#

CREATE PROCEDURE Day(ADate DATE)
RETURNS (Result INTEGER)
AS
BEGIN
  Result = CAST(CAST(ADate AS VARCHAR(2)) AS INTEGER);
  WHEN ANY DO Result = CAST(CAST(ADate AS VARCHAR(1)) AS INTEGER);
END
#

CREATE PROCEDURE Month(ADate DATE)
RETURNS (Result INTEGER)
AS
DECLARE VARIABLE WorkDate DATE;
DECLARE VARIABLE WorkDay  INTEGER;
BEGIN
  WorkDate = ADate;
  Result = 1;
  WHILE(CAST(WorkDate AS VARCHAR(5)) <> "1-JAN") DO
  BEGIN
    EXECUTE PROCEDURE Day(WorkDate) RETURNING_VALUES WorkDay;
    IF(WorkDay = 1) THEN Result = Result+1;
    WorkDate = WorkDate-1;
  END
END
#

CREATE PROCEDURE EncodeDate(Year INTEGER, Month INTEGER, Day INTEGER)
RETURNS (Result DATE)
AS
BEGIN
  Result = Day || "." || Month || "." || Year;
END
#

CREATE PROCEDURE DecodeDate(ADate DATE)
RETURNS (Year INTEGER, Month INTEGER, Day INTEGER)
AS
BEGIN
  EXECUTE PROCEDURE Year(ADate) RETURNING_VALUES Year;
  EXECUTE PROCEDURE Month(ADate) RETURNING_VALUES Month;
  EXECUTE PROCEDURE Day(ADate) RETURNING_VALUES Day;
END
#

CREATE PROCEDURE ElapsedDays(Date1 DATE, Date2 DATE)
RETURNS (Result INTEGER)
AS
BEGIN
  Result = Date2-Date1;
END
#

/* This procedure calculates the modulus of two number */
CREATE PROCEDURE Modulus(Dividend INTEGER, Divisor INTEGER)
RETURNS (Result INTEGER)
AS
BEGIN
  IF(Dividend = 0) THEN Result = 0;
  ELSE
    Result = Dividend-(CAST((Dividend / Divisor)-0.5 AS INTEGER)*Divisor);
END
#

/* This procedure returns the number of the day of the week:
   0 - Monday; 1 - Tuesday; 2 - Wdnesday; 3 - Thursday; 4 - Friday; 5 - Saturday; 6 - Sunday
*/   
CREATE PROCEDURE DayOfWeek(ADate DATE)
RETURNS (Result INTEGER)
AS
DECLARE VARIABLE Elapsed INTEGER;
BEGIN
  EXECUTE PROCEDURE ElapsedDays("1.1.96", ADate) RETURNING_VALUES Elapsed;
  EXECUTE PROCEDURE Modulus(Elapsed, 7) RETURNING_VALUES Result;
END
#

CREATE PROCEDURE Time(ADate DATE)
RETURNS (Hour INTEGER, Minute INTEGER)
AS
DECLARE VARIABLE Year     INTEGER;
DECLARE VARIABLE Month    INTEGER;
DECLARE VARIABLE Day      INTEGER;
DECLARE VARIABLE WorkDate DATE;
DECLARE VARIABLE Percent  DOUBLE PRECISION;
DECLARE VARIABLE WorkHour DOUBLE PRECISION;
BEGIN
  EXECUTE PROCEDURE DecodeDate(ADate) RETURNING_VALUES(Year, Month, Day);
  EXECUTE PROCEDURE EncodeDate(Year, Month, Day) RETURNING_VALUES(WorkDate);
  Percent = ADate - WorkDate;
  WorkHour = Percent *24;
  Hour = WorkHour -0.5;
  Minute = (WorkHour - Hour) *60 -0.5;
END
#

CREATE PROCEDURE DaysOfMonth(AYear INTEGER, AMonth INTEGER)
RETURNS (Result INTEGER)
AS
DECLARE VARIABLE WorkDate   DATE;
BEGIN
  Result = 31;
  WHILE (Result > 28 AND WorkDate IS NULL) DO
  BEGIN
    EXECUTE PROCEDURE EncodeDate(AYear, AMonth, Result) RETURNING_VALUES(WorkDate);
    WHEN ANY DO Result = Result -1;
  END
END
#

CREATE PROCEDURE DayOfYear(ADate DATE)
RETURNS (Result INTEGER)
AS
DECLARE VARIABLE WorkYear   INTEGER;
DECLARE VARIABLE WorkMonth  INTEGER;
DECLARE VARIABLE I          INTEGER;
BEGIN
  Result = 0;
  EXECUTE PROCEDURE Year(ADate) RETURNING_VALUES(WorkYear);  
  EXECUTE PROCEDURE Month(ADate) RETURNING_VALUES(WorkMonth);  
  WorkMonth = WorkMonth-1;  
  WHILE (WorkMonth > 0) DO
  BEGIN
    EXECUTE PROCEDURE DaysOfMonth(WorkYear, WorkMonth) RETURNING_VALUES(I); 
    Result = Result+I; 
    WorkMonth = WorkMonth-1; 
  END
  EXECUTE PROCEDURE Day(ADate) RETURNING_VALUES(I); 
  Result = Result +I;
END
#

/* 
  This procedure is based on the idea of George Kozaderov.
  Address: 19-17, Engels st.,Ryazan, 390010, Russia
  e-mail:  relmail@geo.ryazan.ru
*/
CREATE PROCEDURE AddMonth(ADate DATE, AMonth INTEGER)
RETURNS (Result DATE)
AS
DECLARE VARIABLE Year           INTEGER;
DECLARE VARIABLE Month          INTEGER;
DECLARE VARIABLE Day            INTEGER;
DECLARE VARIABLE Mod            INTEGER;
DECLARE VARIABLE DaysOfMOnth    INTEGER;
BEGIN
  EXECUTE PROCEDURE DecodeDate(ADate) RETURNING_VALUES(Year, Month, Day);
  IF(AMonth < 0) THEN
  BEGIN
    AMonth = AMonth * -1;
    Year = Year - CAST(((AMonth-1)/12)-0.49 AS INTEGER) -1;
    
    AMonth = AMonth -1;
    EXECUTE PROCEDURE Modulus(AMonth, 12) RETURNING_VALUES(Mod);
    AMonth = 12 - (Mod+1);
    Month = Month + AMonth;
  END
  ELSE
  BEGIN
    Year = Year + CAST(((AMonth-1)/12)-0.49 AS INTEGER);
    
    AMonth = AMonth -1;
    EXECUTE PROCEDURE Modulus(AMonth, 12) RETURNING_VALUES(Mod);
    Month = Month + Mod +1;
  END
  
  IF(Month > 12) THEN
  BEGIN
    Year = Year +1;
    Month = Month -12;
  END
  
  EXECUTE PROCEDURE DaysOfMonth(Year, Month) RETURNING_VALUES (DaysOfMonth);
  IF(Day > DaysOfMonth) THEN Day = DaysOfMonth;
  
  EXECUTE PROCEDURE EncodeDate(Year, Month, Day) RETURNING_VALUES (Result);
END
#

SET TERM ; #

