Y2K Date Procedures in PL/I:
DATETIME, DAYS, DAYSTODATE, DAYSTOSECS, REPATTERN, SECS, SECSTODATE, etc.
/* Copyright (c) 1995, 2002 by R. A. Vowels, from "Introduction to PL/I, Algorithms, and */
/* Structured Programming". Permission is given to reproduce and to use these procedures */
/* as part of a program, and to include them as part of a larger work to be sold for profit. */
/* However, the user is not permitted to sell the procedures separately. Provided always */
/* that these procedures and this copyright notice are reproduced in full. */
/* Unless hours were cups of sack, and minutes capons, and clocks the tongues of bawds, . . . */
/* I see no reason why thou should be so superfluous to demand the time of the day. */
/* - Shakespeare, Henry IV Part 1, I, ii */
/* These procedures implement the following date functions: */
/* DATETIME, DAYS, DAYSTODATE, DAYSTOSECS, REPATTERN, SECS, SECSTODATE, */
/* SECSTODAYS, VALIDDATE, WEEKDAY, Y4DATE, Y4JULIAN, Y4YEAR. */
/* Also implemented is the function TODAY. */
/* Updated 4 July 2002. */
/* This procedure prints the day, date, month, and year of today's date. */
(SUBSCRIPTRANGE, CONVERSION):
TODAY:
PROCEDURE;
DECLARE MONTHS(12) CHARACTER (9) VARYING STATIC INITIAL
( 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November',
'December' );
DECLARE DAYS (0:6) CHARACTER (9) VARYING STATIC INITIAL
( 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday' );
DECLARE (YEAR, MONTH, DAY) FIXED BINARY;
DECLARE DAY_CODE CHARACTER (6);
DECLARE (DATE, SUBSTR) BUILTIN;
/* This function procedure returns the current day of the week as an integer (0 = Sunday, */
/* 1 = Monday, etc). */
WEEKDAY:
PROCEDURE RETURNS (FIXED BINARY);
DECLARE DW FIXED BINARY,
T(1:12) FIXED BINARY (7) STATIC INITIAL
(1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6);
DECLARE (MOD, BINARY) BUILTIN;
IF YEAR < 96 THEN
YEAR = YEAR + 100;
DW = MOD (BINARY(YEAR*5/4, 15, 0) + T(MONTH) + DAY + 6, 7);
IF (MOD (YEAR, 4) = 0) & (MONTH <= 2) THEN
IF YEAR > 0 THEN
DW = DW - 1; /* No decrement for 1900 */
IF DW < 0 THEN
DW = DW + 7;
RETURN (DW);
END WEEKDAY;
ON CONVERSION SNAP GO TO AVOID;
DAY_CODE = DATE;
YEAR = SUBSTR (DAY_CODE, 1, 2);
MONTH = SUBSTR (DAY_CODE, 3, 2);
DAY = SUBSTR (DAY_CODE, 5, 2);
PUT SKIP EDIT ('Today is ', DAYS(WEEKDAY()), DAY, MONTHS(MONTH), YEAR+1900 )
(2 A, F(3), X(1), A, F(5));
AVOID:
END TODAY;
/* This function procedure returns today's date in the readable form DD Mmm YYYY. */
NEW_DATE:
PROCEDURE RETURNS (CHARACTER (11) );
DECLARE Squashed_date CHARACTER (9);
Squashed_date = DATETIME ( 'DDMmmYYYY' );
RETURN (SUBSTR (Squashed_date, 1, 2) || ' ' ||
SUBSTR (Squashed_date, 3, 3) || ' ' ||
SUBSTR (Squashed_date, 6, 4) );
END NEW_DATE;
/**************************************************************** */
/* Generic procedures to implement the WEEKDAY built-in function. */
/**************************************************************** */
DECLARE WEEKDAY GENERIC
(WEEKDAY0 WHEN ( ),
WEEKDAY1 WHEN (*) );
/* This function procedure returns the current day of the week as an integer */
/* (1 = Sunday, 2 = Monday, etc). */
WEEKDAY0:
PROCEDURE RETURNS (FIXED BINARY (31) );
DECLARE DW FIXED BINARY,
T(1:12) FIXED BINARY (7) STATIC INITIAL
(1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6);
DECLARE (YEAR, MONTH, DAY) FIXED BINARY;
DECLARE DAY_CODE CHARACTER (6);
DECLARE (DATE, SUBSTR,
MOD, BINARY) BUILTIN;
DAY_CODE = DATE;
YEAR = SUBSTR (DAY_CODE, 1, 2);
MONTH = SUBSTR (DAY_CODE, 3, 2);
DAY = SUBSTR (DAY_CODE, 5, 2);
IF YEAR < 96 THEN
YEAR = YEAR + 100;
DW = MOD (BINARY (YEAR*5/4, 15, 0) + T(MONTH) + DAY + 6, 7);
IF (MOD (YEAR, 4) = 0) & (MONTH <= 2) THEN
IF YEAR > 0 THEN
DW = DW - 1; /* No decrement for 1900 */
/* At this point, DW = 0 for Sunday, 1 for Monday, etc. */
IF DW < 0 THEN
DW = DW +7;
RETURN (DW+1); /* Here, 1 = Sunday, 2 = Monday, etc. */
END WEEKDAY0;
/* This function procedure returns the current day of the week as an integer */
/* (1 = Sunday, 2 = Monday, etc). */
WEEKDAY1:
PROCEDURE (DAY_No) RETURNS (FIXED BINARY (31) );
DECLARE DAY_No FIXED BINARY (31);
DECLARE DW FIXED BINARY,
T(1:12) FIXED BINARY (7) STATIC INITIAL
(1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6);
DECLARE CENTURY FIXED BINARY (7);
DECLARE (YEAR, MONTH, DAY) FIXED BINARY;
DECLARE DAY_CODE CHARACTER (8);
DECLARE (DATE, SUBSTR,
MOD, BINARY) BUILTIN;
DAY_CODE = DAYSTODATE (DAY_No, 'YYYYMMDD' );
CENTURY = SUBSTR (DAY_CODE, 1, 2);
YEAR = SUBSTR (DAY_CODE, 3, 2);
YEAR = YEAR + (CENTURY-19)*100;
MONTH = SUBSTR (DAY_CODE, 5, 2);
DAY = SUBSTR (DAY_CODE, 7, 2);
DW = MOD (BINARY (YEAR*5/4, 15, 0) + T(MONTH) + DAY + 6, 7);
IF (MOD (YEAR, 4) = 0) & (MONTH <= 2) THEN
IF YEAR > 0 THEN /* No decrement for 1900. */
DW = DW - 1;
/* At this point, DW = 0 for Sunday, 1 for Monday, etc. */
IF DW < 0 THEN
DW = DW +7;
RETURN (DW+1); /* Here, 1 = Sunday, 2 = Monday, etc. */
END WEEKDAY1;
/* These generic procedures implement the DATETIME built-in function. */
DECLARE DATETIME GENERIC /* {XE "INCLUDE files: DATETIME"} */
(DATETIME1 WHEN ( ),
DATETIME2 WHEN (*) );
/* This function procedure returns the date & time in default DATETIME format. */
DATETIME1:
PROCEDURE RETURNS (CHARACTER (17));
DECLARE (DATE, TIME, SUBSTR) BUILTIN;
DECLARE TDATE CHARACTER (6);
DECLARE (Y, CENTURY) CHARACTER (2);
TDATE = DATE;
Y = SUBSTR (TDATE, 1, 2);
IF Y >= '98' THEN
CENTURY = '19';
ELSE
CENTURY = '20';
RETURN (CENTURY || TDATE || TIME);
END DATETIME1;
/* This function procedure returns the date, or date & time, */
/* in the form specified by the template format. */
DATETIME2:
PROCEDURE (DATE_FORMAT) RETURNS (CHARACTER (17) VARYING);
DECLARE DATE_FORMAT CHARACTER (*);
DECLARE HBOUND BUILTIN;
DECLARE J FIXED BINARY;
DECLARE FORMS (37) CHARACTER (17) VARYING STATIC INITIAL (
'YYYYMMDDHHMISS999', 'YYYYMMDD', 'YYYYMMMDD',
'YYYYMmmDD', 'YYYYDDD', 'YYYYMM',
'YYYYMMM', 'YYYYMmm', 'YYYY',
'YYMMDD', 'YYMMMDD', 'YYMmmDD',
'YYDDD', 'YYMM', 'YYMMM',
'YYMmm', 'YY',
'MMDDYYYY', 'MMMDDYYYY', 'MmmDDYYYY',
'MMYYYY', 'MMMYYYY', 'MmmYYYY',
'MMDDYY', 'MMMDDYY', 'MmmDDYY',
'MMYY', 'MMMYY', 'MmmYY',
'DDMMYYYY', 'DDMMMYYYY', 'DDMmmYYYY',
'DDDYYYY', 'DDMMYY', 'DDMMMYY',
'DDMmmYY', 'DDDYY' );
DO J = 1 TO HBOUND (FORMS, 1); /* Search the list of permitted templates. */
IF DATE_FORMAT = FORMS (J) THEN
RETURN (MAKE_DATETIME (J) );
END;
PUT SKIP EDIT ( 'The template ', DATE_FORMAT, ' is not one of the permitted forms.' )
(A);
SIGNAL ERROR;
END DATETIME2;
/* This procedure returns the date, or the date and time in one of 37 formats, selected */
/* according to the value of N. */
MAKE_DATETIME:
PROCEDURE (N) RETURNS (CHARACTER(17) VARYING);
/* INCOMING: N = selector value, in the range 1 to 23. */
DECLARE N FIXED BINARY;
DECLARE (DATE, TIME,
SUBSTR) BUILTIN;
DECLARE TDATE CHARACTER (6);
DECLARE TTIME CHARACTER (9);
DECLARE (D, MM, YY,
CENTURY) CHARACTER (2);
TDATE = DATE; TTIME = TIME;
YY = SUBSTR (TDATE, 1, 2);
IF YY >= '95' THEN
CENTURY = '19';
ELSE
CENTURY = '20';
/* The first argument is converted to a 4-digit year prior to the call. */
RETURN ( MAKE_DATETIMES (CENTURY || TDATE, N) );
END MAKE_DATETIME;
/* This function returns the day number within the current year. */
DOY:
PROCEDURE RETURNS (CHARACTER (3));
DECLARE (DATE,
LENGTH, SUBSTR) BUILTIN;
DECLARE TDATE CHARACTER (6);
DECLARE (J, DAYS, M) FIXED BINARY;
DECLARE SDAYS CHARACTER (30) VARYING;
DECLARE DM(1:12) FIXED BINARY (7) STATIC INITIAL
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31 ,30 ,31);
TDATE = DATE;
M = SUBSTR (TDATE, 3, 2);
DAYS = SUBSTR (TDATE, 5, 2);
DO J = 1 TO M-1;
DAYS = DAYS + DM(J);
END;
IF MOD (SUBSTR (TDATE, 1, 2), 4 ) = 0 THEN /* It's a leap year. */
IF DAYS > 59 THEN
DAYS = DAYS + 1;
SDAYS = DAYS;
J = LENGTH (SDAYS);
RETURN (SUBSTR (SDAYS, J-2, 3));
END DOY;
/* This function returns the day number of the date specified, from the start of that year. */
DOY1:
PROCEDURE (TDATE) RETURNS (CHARACTER (3));
DECLARE TDATE CHARACTER (*);
DECLARE (DATE,
LENGTH, SUBSTR) BUILTIN;
DECLARE (J, DAYS, M) FIXED BINARY;
DECLARE SDAYS CHARACTER (30) VARYING;
DECLARE DM(1:12) FIXED BINARY (7) STATIC INITIAL
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31 ,30 ,31);
M = SUBSTR (TDATE, 5, 2);
DAYS = SUBSTR (TDATE, 7, 2);
DO J = 1 TO M-1;
DAYS = DAYS + DM(J);
END;
IF MOD (SUBSTR (TDATE, 3, 2), 4 ) = 0 THEN /* It's a leap year. */
IF DAYS > 59 THEN
DAYS = DAYS + 1;
SDAYS = DAYS;
J = LENGTH (SDAYS);
RETURN (SUBSTR (SDAYS, J-2, 3));
END DOY1;
DECLARE DAYS GENERIC /* {XE "INCLUDE files: DAYS"} */
(DAYS0 WHEN ( ),
DAYS1 WHEN (*),
DAYS2 WHEN (*, *),
DAYS3 WHEN (*, *, *) );
/* This function procedure returns the number of days since the beginning of time. */
/* Dates allowed start from 1 January 1900. */
DAYS0:
PROCEDURE RETURNS (FIXED BINARY (31));
DECLARE (DATE, SUBSTR,
DIVIDE) BUILTIN;
DECLARE TDATE CHARACTER (6);
DECLARE (Y, CENTURY) FIXED BINARY;
DECLARE NDAYS FIXED BINARY (31);
TDATE = DATE;
Y = SUBSTR (TDATE, 1, 2);
IF Y >= 96 THEN
CENTURY = 19;
ELSE
CENTURY = 20;
NDAYS = (CENTURY*100 + Y - 1900)*365 /* The number of days in whole years . . . */
+ DIVIDE (Y-1, 4, 15, 0) /* the number of leap days . . . */
+ DOY(); /* the number of days so far in the current year.*/
RETURN (NDAYS + 115860);
END DAYS0;
/* This function procedure returns the number of days since the beginning of time */
/* until the date TDATE (assumed to be the default for DATETIME). */
/* Dates allowed start from 1 January 1900. */
DAYS1:
PROCEDURE (TDATE) RETURNS (FIXED BINARY (31));
DECLARE TDATE CHARACTER (*);
DECLARE (SUBSTR,
DIVIDE) BUILTIN;
DECLARE Y FIXED BINARY;
DECLARE NDAYS FIXED BINARY (31);
Y = SUBSTR (TDATE, 1, 4);
NDAYS = (Y - 1900)*365 /* The number of days in whole years . . . */
+ DIVIDE (Y-1900-1, 4, 15, 0) /* the number of leap days . . . */
+ DOY1(TDATE); /* the number of days so far in the current year.*/
RETURN (NDAYS + 115860);
END DAYS1;
/* This function procedure returns the number of days since the beginning of time */
/* to the date TDATE (given in the specified format). */
/* Dates allowed start from 1 January 1900. */
DAYS2:
PROCEDURE (TDATE, LAYOUT) RETURNS (FIXED BINARY (31));
DECLARE TDATE CHARACTER (*),
LAYOUT CHARACTER (*);
RETURN (DAYS3(TDATE, LAYOUT, 1950) ); /* Assume that the window starts at 1950. */
END DAYS2;
/* This function procedure returns the number of days since the beginning of time */
/* to the date TDATE (given in the specified format). */
/* Dates allowed start from 1 January 1900. */
/* This procedure accepts a date window, for use with a 2-digit year. */
DAYS3:
PROCEDURE (TDATE, LAYOUT, Window) RETURNS (FIXED BINARY (31));
/* INCOMING: TDATE = A date, in a format corresponding to: */
/* LAYOUT = One of the standard date patterns; and */
/* Window = a 4-digit year, being the beginning of the 100-year window. */
/* If the 2-digit year specified in TDATE is less than */
/* the 2-digit component of the 4-digit year in Window, then the */
/* century is increnemted. */
/* E.G., if Window = 1970, then dates 70 to 99 are 1970 to 1999, while */
/* dates 00 through 69 are 2000 through 2069. */
DECLARE TDATE CHARACTER (*),
LAYOUT CHARACTER (*);
DECLARE Window FIXED BINARY;
DECLARE NSDAYS CHARACTER (2);
DECLARE Year CHARACTER (4),
STD_DATE CHARACTER(8);
DECLARE S CHARACTER (17);
DECLARE Ndays FIXED BINARY (31);
DECLARE (K, M, Y) FIXED BINARY;
DECLARE MONTHC (12) CHARACTER(3) STATIC INITIAL (
'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC' );
DECLARE MONTH (12) CHARACTER(3) STATIC INITIAL (
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' );
S = DATETIME2 (LAYOUT); /* Checks the layout, but discards the returned date. */
K = INDEX(LAYOUT, 'YY' );
IF K = 0 THEN
DO;
PUT SKIP EDIT ( '***ERROR, Invalid pattern ', Layout ) (A);
SIGNAL ERROR;
END;
IF SUBSTR (LAYOUT, K, 4) = 'YYYY' THEN /* We have a 4-digit year. */
Y, Year = SUBSTR (TDATE, K, 4);
ELSE /* We have a 2-digit year, so ... */
Y, Year = Y4YEAR ( SUBSTR (TDATE, K, 2), Window); /* ... expand the year to 4 digits, */
/* with a 100-year window from the year "Window".*/
IF Y < 1900 THEN
DO;
PUT SKIP LIST ( '***ERROR, the year is earlier than 1900.' );
SIGNAL ERROR;
END;
K = INDEX (Layout, 'M' );
IF SUBSTR (Layout, K+1, 1) = 'm' THEN /* Lower-case month is always 3 characters. */
DO M = 1 TO 12;
IF Month(M) = SUBSTR(TDATE, K, 3) THEN LEAVE;
END;
ELSE IF LENGTH(Layout) >= K+2 THEN /* We can look for 3-character months. */
DO;
IF SUBSTR(Layout, K+2, 1) = 'M' THEN /* We have 3-character upper-case months. */
DO M = 1 TO 12;
IF Monthc(M) = SUBSTR (TDATE, K, 3) THEN LEAVE;
END;
ELSE /* We have 2-digit months. */
M = SUBSTR(TDATE, K, 2);
END;
ELSE /* The only other possibility is 2-digit months.*/
M = SUBSTR(TDATE, K, 2);
IF M > 12 | M = 0 THEN
DO;
PUT SKIP EDIT ( 'The month is out of range in the date ', TDATE) (A);
SIGNAL ERROR;
END;
K = INDEX(Layout, 'DD' );
NSdays = SUBSTR (TDATE, K, 2);
PUT STRING (STD_DATE) EDIT (Year, M, NSdays) (A, F(2), A);
NDAYS = (Y - 1900)*365 /* The number of days in whole years . . . */
+ DIVIDE (Y-1900-1, 4, 15, 0) /* the number of leap days . . . */
+ DOY1(STD_DATE); /* the number of days so far in the current year. */
RETURN (Ndays + 115860);
END DAYS3;
/********************************************************************* */
/* Generic procedures to implement the DAYSTODATE function. */
/********************************************************************* */
DECLARE DAYSTODATE GENERIC /* {XE "INCLUDE files: DAYSTODATE"} */
(DAYSTODATE_long WHEN (*),
DAYSTODATE_specific WHEN (*,*),
DAYSTODATE_windowed WHEN (*,*, *) );
/* This procedure converts the number of days PDAYS to a character string date in the */
/* default DATETIME format YYYYMMDDHHMISS999. */
/* Modified 4 July 2002. */
DAYSTODATE_long:
PROCEDURE (PDAYS) RETURNS (CHARACTER(17) );
/* INCOMING: DAYS = the number of days since the beginning of time. */
DECLARE PDAYS FIXED BINARY (31);
DECLARE DAYS FIXED BINARY (31);
DECLARE (TIME, MOD) BUILTIN;
DECLARE SDATE CHARACTER (8);
DECLARE NDAYS FIXED BINARY (31);
DECLARE (ND, NYEARS,
DAYNO, MONTH) FIXED BINARY;
DECLARE DM(1:12) FIXED BINARY (7) STATIC INITIAL
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31 ,30 ,31);
DAYS = PDAYS - 6287; /* Adjust to 1600. */
IF DAYS < 0 THEN
DO;
PUT SKIP LIST ('Implementation restriction: days earlier than 1600 are not permitted.');
SIGNAL ERROR;
END;
NDAYS, NYEARS = 0;
DO WHILE (NDAYS < DAYS);
ND = 365;
NDAYS = NDAYS + 365;
IF ((MOD (NYEARS, 4) = 0) & (MOD (NYEARS, 100) ^= 0)) | (MOD(NYears, 400) = 0) THEN
DO; /* It's a leap year. */
NDAYS = NDAYS + 1;
ND = ND + 1;
END;
NYEARS = NYEARS + 1;
END;
NDAYS = NDAYS - ND; NYEARS = NYEARS - 1;
NDAYS = DAYS - NDAYS;
/* Determine the month and day for the year requested. */
IF ((MOD (NYEARS, 4) = 0) & (MOD (NYEARS, 100) ^= 0)) | (MOD(NYears, 400) = 0) THEN
DM(2) = 29;
ELSE
DM(2) = 28;
DAYNO = 0;
DO MONTH = 1 TO 12 WHILE (DAYNO + DM(MONTH) < NDAYS);
DAYNO = DAYNO + DM(MONTH);
END;
DAYNO = NDAYS - DAYNO;
PUT STRING (SDATE) EDIT (1600 + NYEARS, MONTH, DAYNO) (P'9999', 2 P'99' );
RETURN (SDATE || TIME);
END DAYSTODATE_long;
/* This procedure converts the number of days PDAYS to a character string date in the */
/* specific format given by LAYOUT. */
DAYSTODATE_specific:
PROCEDURE (PDAYS, LAYOUT) RETURNS (CHARACTER(17) VARYING);
/* INCOMING: PDAYS = the number of days since the beginning of time. */
/* LAYOUT = one of the permissible date/time formats. */
DECLARE PDAYS FIXED BINARY (31);
DECLARE LAYOUT CHARACTER (*);
RETURN (DAYSTODATE_Windowed (PDAYS, LAYOUT, 1950) );
END DAYSTODATE_specific;
/* This procedure converts the number of days PDAYS to a character string date in the */
/* specific format given by LAYOUT. */
DAYSTODATE_windowed:
PROCEDURE (PDAYS, LAYOUT, Window) RETURNS (CHARACTER(17) VARYING);
/* INCOMING: PDAYS = the number of days since the beginning of time. */
/* LAYOUT = one of the permissible date/time formats. */
/* Window = a 4-digit year, being the beginning of the 100-year window. */
/* If the 2-digit year specified in TDATE is less than */
/* the 2-digit component of the 4-digit year in Window, then the */
/* century is increnemted. */
/* E.G., if Window = 1970, then dates 70 to 99 are 1970 to 1999, while */
/* dates 00 through 69 are 2000 through 2069. */
/* Modified 4 July 2002. */
DECLARE PDAYS FIXED BINARY (31);
DECLARE LAYOUT CHARACTER (*);
DECLARE DAYS FIXED BINARY (31);
DECLARE (TIME, MOD) BUILTIN;
DECLARE SDATE CHARACTER (8);
DECLARE NDAYS FIXED BINARY (31);
DECLARE (ND, NYEARS,
DAYNO, MONTH) FIXED BINARY;
DECLARE DM(1:12) FIXED BINARY (7) STATIC INITIAL
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31 ,30 ,31);
DAYS = PDAYS - 6287; /* Adjust to 1600. */
IF DAYS < 0 THEN
DO;
PUT SKIP LIST ('Implementation restriction: days earlier than 1600 are not permitted.');
SIGNAL ERROR;
END;
NDAYS, NYEARS = 0;
DO WHILE (NDAYS < DAYS);
ND = 365;
NDAYS = NDAYS + 365;
IF ((MOD (NYEARS, 4) = 0) & (MOD (NYEARS, 100) ^= 0)) | (MOD(NYears, 400) = 0) THEN
DO; /* It's a leap year. */
NDAYS = NDAYS + 1;
ND = ND + 1;
END;
NYEARS = NYEARS + 1;
END;
NDAYS = NDAYS - ND; NYEARS = NYEARS - 1;
NDAYS = DAYS - NDAYS;
/* Determine the month and day for the year requested. */
IF ((MOD (NYEARS, 4) = 0) & (MOD (NYEARS, 100) ^= 0)) | (MOD(NYears, 400) = 0) THEN
DM(2) = 29;
ELSE
DM(2) = 28;
DAYNO = 0;
DO MONTH = 1 TO 12 WHILE (DAYNO + DM(MONTH) < NDAYS);
DAYNO = DAYNO + DM(MONTH);
END;
DAYNO = NDAYS - DAYNO;
IF Window < 0 THEN
NYEARS = NYEARS - Window;
PUT STRING (SDATE) EDIT (1600 + NYEARS, MONTH, DAYNO) (P'9999', 2 P'99' );
RETURN (DATETIMES (SDATE, LAYOUT) );
END DAYSTODATE_windowed;
/* This function procedure compares the given date/time format with the list of 37 */
/* possible formats. */
DATETIMES:
PROCEDURE (SDATE, DATE_FORMAT) RETURNS (CHARACTER (17) VARYING);
/* INCOMING: SDATE = the date, as a string, in the form produced by the DATE built-in */
/* function. */
/* DATE_FORMAT = the pro-forma of one of the permissible 23 formats for date/time. */
DECLARE SDATE CHARACTER (8);
DECLARE DATE_FORMAT CHARACTER (*);
DECLARE HBOUND BUILTIN;
DECLARE J FIXED BINARY;
DECLARE FORMS (37) CHARACTER (17) VARYING STATIC INITIAL (
'YYYYMMDDHHMISS999', 'YYYYMMDD', 'YYYYMMMDD',
'YYYYMmmDD', 'YYYYDDD', 'YYYYMM',
'YYYYMMM', 'YYYYMmm', 'YYYY',
'YYMMDD', 'YYMMMDD', 'YYMmmDD',
'YYDDD', 'YYMM', 'YYMMM',
'YYMmm', 'YY',
'MMDDYYYY', 'MMMDDYYYY', 'MmmDDYYYY',
'MMYYYY', 'MMMYYYY', 'MmmYYYY',
'MMDDYY', 'MMMDDYY', 'MmmDDYY',
'MMYY', 'MMMYY', 'MmmYY',
'DDMMYYYY', 'DDMMMYYYY', 'DDMmmYYYY',
'DDDYYYY', 'DDMMYY', 'DDMMMYY',
'DDMmmYY', 'DDDYY' );
DO J = 1 TO HBOUND (FORMS, 1); /* Search the list of permissible date/time forms. */
IF DATE_FORMAT = FORMS (J) THEN
RETURN (MAKE_DATETIMES (SDATE, J) );
END;
PUT SKIP EDIT ( 'The template ', DATE_FORMAT, ' is not one of the permitted forms.' )
(A);
SIGNAL ERROR;
END DATETIMES;
/* This function procedure prepares the date, or the date and the time, */
/* in one of the 37 possible formats. */
MAKE_DATETIMES:
PROCEDURE (SDATE, N) RETURNS (CHARACTER(17) VARYING);
/* INCOMING: SDATE = the date, as a string, in the form YYYYMMDD */
/* N = the number of the requested format, in the range 1 <= N <= 23. */
DECLARE SDATE CHARACTER (8);
DECLARE N FIXED BINARY;
DECLARE (DATE, TIME,
SUBSTR) BUILTIN;
DECLARE TDATE CHARACTER (6);
DECLARE TTIME CHARACTER (9);
DECLARE (D, MM, YY,
CENTURY) CHARACTER (2);
DECLARE YYYY CHARACTER (4);
DECLARE M FIXED BINARY;
DECLARE MONTHC (12) CHARACTER (3) STATIC INITIAL (
'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC' );
DECLARE MONTH (12) CHARACTER (3) STATIC INITIAL (
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' );
TDATE = SUBSTR (SDATE, 3); TTIME = TIME;
YY = SUBSTR (TDATE, 1, 2);
CENTURY = SUBSTR (SDATE, 1, 2);
/* Four-digit years, Year first. */
IF N = 1 THEN RETURN ( CENTURY || TDATE || TIME );
IF N = 2 THEN RETURN ( CENTURY || TDATE );
M, MM = SUBSTR (TDATE, 3,2); D = SUBSTR (TDATE, 5,2);
YYYY = CENTURY || YY;
IF N = 3 THEN RETURN ( YYYY || MONTHC(M) || D );
IF N = 4 THEN RETURN ( YYYY || MONTH (M) || D );
IF N = 5 THEN RETURN ( YYYY || DOY( ) );
IF N = 6 THEN RETURN ( YYYY || MM);
IF N = 7 THEN RETURN ( YYYY || MONTHC(M) );
IF N = 8 THEN RETURN ( YYYY || MONTH (M) );
IF N = 9 THEN RETURN ( YYYY );
/* Two-digit years, Year first. */
IF N = 10 THEN RETURN ( TDATE );
IF N = 11 THEN RETURN ( YY || MONTHC(M) || D );
IF N = 12 THEN RETURN ( YY || MONTH (M) || D );
IF N = 13 THEN RETURN ( YY || DOY ( ) );
IF N = 14 THEN RETURN ( YY || MM );
IF N = 15 THEN RETURN ( YY || MONTHC(M) );
IF N = 16 THEN RETURN ( YY || MONTH (M) );
IF N = 17 THEN RETURN (YY);
/* Four-digit years, Month first. */
IF N = 18 THEN RETURN (MM || D || YYYY );
IF N = 19 THEN RETURN (MONTHC(M) || D || YYYY );
IF N = 20 THEN RETURN (MONTH (M) || D || YYYY );
IF N = 21 THEN RETURN (MM || YYYY );
IF N = 22 THEN RETURN (MONTHC(M) || YYYY );
IF N = 23 THEN RETURN (MONTH (M) || YYYY );
/* Two-digit years, Month first. */
IF N = 24 THEN RETURN (MM || D || YY);
IF N = 25 THEN RETURN (MONTHC(M) || D || YY);
IF N = 26 THEN RETURN (MONTH (M) || D || YY);
IF N = 27 THEN RETURN (MM || YY);
IF N = 28 THEN RETURN (MONTHC(M) || YY);
IF N = 29 THEN RETURN (MONTH (M) || YY);
/* Four-digit years, Day first. */
IF N = 30 THEN RETURN (D || MM || YYYY);
IF N = 31 THEN RETURN (D || MONTHC(M) || YYYY);
IF N = 32 THEN RETURN (D || MONTH (M) || YYYY);
IF N = 33 THEN RETURN (DOY( ) || YYYY );
/* Two-digit years, Day first. */
IF N = 34 THEN RETURN (D || MM || YY);
IF N = 35 THEN RETURN (D || MONTHC(M) || YY);
IF N = 36 THEN RETURN (D || MONTH (M) || YY);
IF N = 37 THEN RETURN (DOY( ) || YY);
RETURN ( CENTURY || D); /* Return something. */
END MAKE_DATETIMES;
/****************************************************************** */
/* These generic procedures implement the SECS function. */
/****************************************************************** */
/* A generic procedure to convert a date in DATETIME format, into seconds. */
DECLARE SECS GENERIC (
SECS0 WHEN ( ),
SECS1 WHEN (*),
SECS2 WHEN (*, *),
SECS3 WHEN (*, *, *) );
/* Converts the current (default) date and time to seconds. */
SECS0:
PROCEDURE RETURNS (FLOAT BINARY (53) );
RETURN (SECS(DATETIME () ) );
END SECS0;
/* Converts the specified date in default DATETIME form YYYMMDDHHMISS999 to */
/* seconds. */
SECS1:
PROCEDURE (D) RETURNS (FLOAT BINARY (53) );
DECLARE D CHARACTER (*);
DECLARE 1 T,
2 Hours PICTURE '99',
2 Minutes PICTURE '99',
2 Seconds PICTURE '99V999';
DECLARE X FLOAT BINARY (53);
DECLARE TIME BUILTIN;
X = DAYS(D) * FLOAT(BINARY(24 * 60 * 60));
STRING (T) = TIME;
X = X + (Hours * 3600 + Minutes * 60 + Seconds);
RETURN (X);
END SECS1;
/* Converts the given date in the DATETIME form , into seconds. */
SECS2:
PROCEDURE (D, Pattern) RETURNS (FLOAT BINARY (53) );
DECLARE D CHARACTER (*),
Pattern CHARACTER (*);
RETURN (SECS3 (D, Pattern, 1950) );
END SECS2;
/* Converts the given date in the DATETIME form , into seconds. */
SECS3:
PROCEDURE (D, Pattern, Window) RETURNS (FLOAT BINARY (53) );
/* INCOMING: D = A date, in a format corresponding to: */
/* Pattern = One of the standard date patterns; and */
/* Window = a 4-digit year, being the beginning of the 100-year window. */
/* If the 2-digit year specified in TDATE is less than */
/* the 2-digit component of the 4-digit year in Window, then the */
/* century is increnemted. */
/* E.G., if Window = 1970, then dates 70 to 99 are 1970 to 1999, while */
/* dates 00 through 69 are 2000 through 2069. */
DECLARE D CHARACTER (*),
Pattern CHARACTER (*);
DECLARE Window FIXED BINARY;
DECLARE 1 T,
2 Hours PICTURE '99',
2 Minutes PICTURE '99',
2 Seconds PICTURE '99V999';
DECLARE X FLOAT BINARY (53);
DECLARE TIME BUILTIN;
X = DAYS(D, Pattern, Window) * FLOAT(BINARY(24 * 60 * 60));
IF Pattern ^= 'YYYYMMDDHHMISS999' THEN
RETURN (X);
STRING (T) = SUBSTR (D, LENGTH(D)-8);
X = X + (Hours * 3600 + Minutes * 60 + Seconds);
RETURN (X);
END SECS3;
/******************************************************************* */
/* Generic procedures to implement the SECSTODATE function. */
/******************************************************************* */
DECLARE SECSTODATE GENERIC ( /* {"UTILITY function: SECSTODATE"} */
SECSTODATE1 WHEN (*),
SECSTODATE2 WHEN (*,*) );
/* This function procedure returns the time in default DATETIME format YYYYMMDDHHMISS999. */
SECSTODATE1:
PROCEDURE (Seconds ) RETURNS (CHARACTER (17) VARYING);
DECLARE SECONDS FLOAT BINARY (53);
DECLARE No_of_Days FIXED BINARY (31);
DECLARE No_of_Seconds FIXED BINARY (31);
DECLARE Hours_and_Minutes FIXED BINARY (31);
DECLARE 1 Time,
2 SS999 PICTURE '99999',
2 MM PICTURE '99',
2 HH PICTURE '99';
No_of_Days = Seconds / (60000 * 60 * 24);
No_of_Seconds = MOD (Seconds, 60000 * 60 * 24);
SS999 = MOD (No_of_Seconds, 60000);
Hours_and_Minutes = Seconds / 60000;
MM = MOD (Hours_and_Minutes, 60);
HH = Hours_and_Minutes / 60;
RETURN ( DAYSTODATE(No_of_Days, 'YYYYMMDD' ) || STRING(Time) );
END SECSTODATE1;
/* This function procedure returns the time in default DATETIME format */
/* specified by Pattern. */
SECSTODATE2:
PROCEDURE (Seconds, Pattern) RETURNS (CHARACTER (17) VARYING);
DECLARE Seconds FLOAT BINARY (53);
DECLARE Pattern CHARACTER (*);
DECLARE No_of_Days FIXED BINARY (31);
IF Pattern = 'YYYYMMDDHHMISS999' THEN
RETURN (SECSTODATE (Seconds) );
/* We can forget about fractions of a day. */
No_of_Days = Seconds / (60.0000000000000E3 * 60 * 24 );
RETURN (DAYSTODATE (No_of_Days, Pattern) );
END SECSTODATE2;
/******************************************************************* */
/* Generic procedures to implement the VALIDDATE function. */
/******************************************************************* */
DECLARE VALIDDATE GENERIC (
VALID_DATE WHEN (),
VALID_DATE1 WHEN (*),
VALID_DATE2 WHEN (*,*),
VALID_DATE3 WHEN (*,*,*) );
VALID_DATE:
PROCEDURE RETURNS (BIT (1) );
RETURN (VALID_DATE1 (DATETIME() ) );
END VALID_DATE;
VALID_DATE1:
PROCEDURE (Date) RETURNS (BIT (1) );
DECLARE Date CHARACTER (*);
RETURN ( VALID_DATE2 ( Date, 'YYYYMMDDHHMISS999' ) );
END VALID_DATE1;
VALID_DATE2:
PROCEDURE (Date, Pattern) RETURNS (BIT (1) );
DECLARE Date CHARACTER (*);
DECLARE Pattern CHARACTER (*);
RETURN ( VALID_DATE3 ( Date, Pattern, 1950) );
END VALID_DATE2;
VALID_DATE3:
PROCEDURE (Date, Pattern, Window) RETURNS (BIT (1) );
DECLARE Date CHARACTER (*);
DECLARE Pattern CHARACTER (*);
DECLARE Window FIXED BINARY;
DECLARE HBOUND BUILTIN;
DECLARE MonthC (12) CHARACTER(3) STATIC INITIAL (
'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC' );
DECLARE Month (12) CHARACTER(3) STATIC INITIAL (
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' );
DECLARE Day_Table(12) FIXED BINARY (5) STATIC INITIAL
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
DECLARE (J, K, M) FIXED BINARY;
DECLARE FORMS (37) CHARACTER (17) VARYING STATIC INITIAL (
'YYYYMMDDHHMISS999', 'YYYYMMDD', 'YYYYMMMDD',
'YYYYMmmDD', 'YYYYDDD', 'YYYYMM',
'YYYYMMM', 'YYYYMmm', 'YYYY',
'YYMMDD', 'YYMMMDD', 'YYMmmDD',
'YYDDD', 'YYMM', 'YYMMM',
'YYMmm', 'YY',
'MMDDYYYY', 'MMMDDYYYY', 'MmmDDYYYY',
'MMYYYY', 'MMMYYYY', 'MmmYYYY',
'MMDDYY', 'MMMDDYY', 'MmmDDYY',
'MMYY', 'MMMYY', 'MmmYY',
'DDMMYYYY', 'DDMMMYYYY', 'DDMmmYYYY',
'DDDYYYY', 'DDMMYY', 'DDMMMYY',
'DDMmmYY', 'DDDYY' );
DECLARE (Day_No, Days_In_Month) FIXED BINARY;
DECLARE Century PICTURE '99';
DECLARE Leap_Year_Day FIXED BINARY (1);
DECLARE Y4 CHARACTER (4);
DO J = 1 TO HBOUND (FORMS, 1); /* Search the list of permitted templates. */
IF Pattern = FORMS (J) THEN /* Found a pattern. */
DO; /* Check whether the date is valid. */
K = INDEX (Pattern, 'YYYY');
IF K > 0 THEN /* A 4-digit year. */
DO;
IF VERIFY (SUBSTR (Date, K, 4), '0123456789') ^= 0 THEN
RETURN ('0'B);
IF SUBSTR (Date, K, 4) < '1582' THEN
RETURN ('0'B);
Y4 = SUBSTR (Date, K, 4);
END;
ELSE /* A 2-digit year. */
DO;
K = INDEX (Pattern, 'YY');
IF K > 0 THEN
IF VERIFY (SUBSTR (Date, K, 2), '0123456789') ^= 0 THEN
RETURN ('0'B);
Century = Window/100;
IF SUBSTR (Date, K, 2) < MOD (Window, 100) THEN
Century = Century + 1;
Y4 = Century || SUBSTR (Date, K, 2);
END;
IF MOD (Y4, 400) = 0 THEN Leap_Year_Day = 1;
ELSE IF MOD (Y4, 100) = 0 THEN Leap_Year_Day = 0;
ELSE IF MOD (Y4, 4) = 0 THEN leap_Year_Day = 1;
ELSE Leap_Year_Day = 0;
K = INDEX (Pattern, 'MMM');
IF K > 0 THEN
Month_Loop: DO;
DO M = 1 TO 12;
IF SUBSTR (Date, K, 3) = MonthC(M) THEN
LEAVE Month_Loop;
END;
RETURN ('0'B);
END;
ELSE
DO;
K = INDEX (Pattern, 'MM');
IF K > 0 THEN
DO;
M = SUBSTR (Date, K, 2);
IF (M < 1) | (M > 12) THEN RETURN ('0'B);
END;
END;
K = INDEX (Pattern, 'Mmm');
IF K > 0 THEN
Month_Loop2: DO;
DO M = 1 TO 12;
IF SUBSTR (Date, K, 3) = Month(M) THEN
LEAVE Month_Loop2;
END;
RETURN ('0'B);
END;
K = INDEX (Pattern, 'DDD');
IF K > 0 THEN
DO;
M = SUBSTR (Date, K, 3);
IF (M < 1) | (M > (365+Leap_Year_Day) ) THEN
RETURN ('0'B);
END;
ELSE
DO;
K = INDEX (Pattern, 'DD');
IF K > 0 THEN
DO;
Day_No = SUBSTR (Date, K, 2);
Days_In_Month = Day_Table(M);
IF M = 2 THEN
Days_In_Month = Days_In_Month + Leap_Year_Day;
IF (Day_No < 1) | (Day_No > Days_In_Month) THEN
RETURN ('0'B);
END;
END;
/* If we arrive here, Date must be a valid date. */
RETURN ('1'B);
END;
END;
PUT SKIP EDIT ('***ERROR, the layout ', Pattern, ' is not a standard date pattern.' )
(A);
SIGNAL ERROR; /* Not a standard pattern. */
RETURN ('0'B);
END VALID_DATE3;
/******************************************************************* */
/* Generic procedures to implement the REPATTERN function. */
/******************************************************************* */
DECLARE REPATTERN GENERIC (
REPATTERN_3 WHEN (*, *, *),
REPATTERN_4 WHEN (*, *, *, *) );
REPATTERN_3: /* {XE"INCLUDE files: REPATTERN" } */
PROCEDURE (D, Destination_Pattern, Source_Pattern) RETURNS (CHARACTER(17) VARYING);
/* INCOMING: D = a date (possibly with a two-digit year, lacking the century). */
/* RETURNS: The date in the the form YYYY. The window defaults to 1950. */
/* To ensure that the function performs consistently on systems */
/* that support the WINDOW compiler option, that option */
/* must be set to 1950. */
DECLARE D CHARACTER (*);
DECLARE Source_Pattern CHARACTER (*);
DECLARE Destination_Pattern CHARACTER (*);
RETURN (REPATTERN_4 (D, Destination_Pattern, Source_Pattern, 1950) );
END REPATTERN_3;
REPATTERN_4:
PROCEDURE (A_Date, Destination_Pattern, Source_Pattern, Window) RETURNS (CHARACTER(17) VARYING);
/* INCOMING: A_Date = a date (possibly with a two-digit year, lacking the century). */
/* Destination_Pattern = the date pattern into which A_Date is to be converted; */
/* Source_Pattern = the date pattern corresponding to A_Date; */
/* Window = a 4-digit year, being the beginning of the 100-year window. */
/* If the 2-digit year specified in TDATE is less than */
/* the 2-digit component of the 4-digit year in Window, then the */
/* century is increnemted. */
/* E.G., if Window = 1970, then dates 70 to 99 are 1970 to 1999, while */
/* dates 00 through 69 are 2000 through 2069. */
/* RETURNS: The date in the form specified by Destination_Pattern. */
/* Copyright (c) 2000 by R. A. Vowels. Expanded 4 July 2002. */
DECLARE A_Date CHARACTER (*);
DECLARE Source_Pattern CHARACTER (*);
DECLARE Destination_Pattern CHARACTER (*);
DECLARE Window FIXED BINARY;
DECLARE Date CHARACTER (17) VARYING;
DECLARE Year FIXED BINARY;
DECLARE Century PICTURE '99';
DECLARE Time CHARACTER (9);
DECLARE New_Form CHARACTER (8);
DECLARE Final_Form CHARACTER (17) VARYING;
DECLARE No_of_Days FIXED BINARY (31);
DECLARE K FIXED BINARY;
/* Method: 1. Convert the date to the number of days; */
/* 2. Convert the days to the fixed date form YYYYMMDD. */
/* 3. Adjust the century; */
/* 4. Convert the date to the number of days. */
/* 5. Convert the days to a date in the new format. */
Date = TRIM (A_Date, ' ', ''); /* A_Date is permitted to have leading blanks only. */
No_of_Days = DAYS (Date, Source_Pattern);
IF Source_Pattern = 'YYYYMMDDHHMISS999' THEN
Time = SUBSTR (Date, 9);
ELSE
Time = (9)'0';
New_Form = DAYSTODATE (No_of_Days, 'YYYYMMDD');
/* Gets the date in a fixed form. */
IF INDEX (Source_Pattern, 'YYYY') = 0 THEN /* A_Date has a two-digit year. */
DO; /* Convert the year to a 4-digit year. */
Year = SUBSTR(New_Form, 1, 2); /* Extract the year. */
Century = Window/100;
IF Year < MOD(Window, 100) THEN
Century = Century + 1;
SUBSTR (New_Form, 1, 2) = Century; /* Substitute the new century. */
No_of_Days = DAYS (New_Form, 'YYYYMMDD' ); /* Convert to days again. */
END;
IF INDEX (Destination_Pattern, 'YYYY') = 0 THEN /* The destination has 2-digit year. */
DO; /* Check that the year is in the required date window. */
IF (SUBSTR (New_Form, 1, 4) < Window) &
(SUBSTR (new_Form, 1, 4) > Window + 99) THEN
DO;
PUT SKIP LIST ('The date is outside the current data window.');
SIGNAL ERROR;
END;
END;
Final_Form = DAYSTODATE (No_of_Days, Destination_Pattern);
/* Convert to the new pattern. */
IF Destination_Pattern = 'YYYYMMDDHHMISS999' THEN
RETURN (SUBSTR(Final_Form, 1, 8) || Time);
ELSE
RETURN (Final_Form);
END REPATTERN_4;
/******************************************************************* */
/* Generic procedures to implement the Y4DATE function. */
/******************************************************************* */
DECLARE Y4DATE GENERIC ( /* {XE"UTILITY function: Y4DATE"} */
Y4DATE_1 WHEN (*),
Y4DATE_2 WHEN (*, *));
Y4DATE_1:
PROCEDURE (D) RETURNS (CHARACTER(8) );
/* INCOMING: D = a date in DATE () format (YYMMDD), with a 2-digit year. */
/* RETURNS: The date in the the form YYYYMMDD. The window defaults to 1950. */
/* To ensure that the function performs consistently on systems */
/* that support the WINDOW compiler option, that option */
/* must be set to 1950. */
DECLARE D CHARACTER (*);
RETURN (Y4DATE_2 (D, 1950) );
END Y4DATE_1;
Y4DATE_2:
PROCEDURE (D, Window) RETURNS (CHARACTER(8) );
/* INCOMING: D = a date in DATE () format (YYMMDD), with a 2-digit year. */
/* Window = a 4-digit year from which the year will be windowed. */
/* RETURNS: The date in the the form YYYYMMDD. */
DECLARE D CHARACTER (*);
DECLARE Window FIXED BINARY;
DECLARE Year FIXED BINARY;
DECLARE Century PICTURE '99';
Year = SUBSTR (D, 1, 2); /* Extract the year. */
Century = Window/100;
IF Year < MOD(Window, 100) THEN
Century = Century + 1;
RETURN (Century || D);
END Y4DATE_2;
/******************************************************************* */
/* Generic procedures to implement the Y4JULIAN function. */
/******************************************************************* */
DECLARE Y4JULIAN GENERIC ( /* {XE"UTILITY function: Y4JULIAN"} */
Y4JULIAN_1 WHEN (*),
Y4JULIAN_2 WHEN (*, *));
Y4JULIAN_1:
PROCEDURE (D) RETURNS (CHARACTER (8) );
/* INCOMING: D = a date in DATE () format (YYMMDD), with a 2-digit year. */
/* RETURNS: The date in the the form YYYYMMDD. The window defaults to 1950. */
/* To ensure that the function performs consistently on systems */
/* that support the WINDOW compiler option, that option */
/* must be set to 1950. */
DECLARE D CHARACTER (*);
RETURN (Y4JULIAN_2 (D, 1950) );
END Y4JULIAN_1;
Y4JULIAN_2:
PROCEDURE (D, Window) RETURNS (CHARACTER(8) );
/* INCOMING: D = a date in DATE () format (YYMMDD), with a 2-digit year. */
/* Window = a 4-digit year from which the year will be windowed. */
/* RETURNS: The date in the the form YYYYMMDD. */
DECLARE D CHARACTER (*);
DECLARE Window FIXED BINARY;
DECLARE Year FIXED BINARY;
DECLARE Century PICTURE '99';
Year = SUBSTR (TRIM(D), 1, 2); /* Extract the year. */
Century = Window/100;
IF Year < MOD(Window, 100) THEN
Century = Century + 1;
RETURN (Century || D);
END Y4JULIAN_2;
/******************************************************************* */
/* Generic procedures to implement the Y4YEAR function. */
/******************************************************************* */
DECLARE Y4YEAR GENERIC (
Y4YEAR_1 WHEN (*),
Y4YEAR_2 WHEN (*, *));
Y4YEAR_1: /* {XE" UTILITY function: Y4YEAR" } */
PROCEDURE (D) RETURNS (CHARACTER(4) );
/* INCOMING: D = a two-digit year, lacking the century. */
/* RETURNS: The year in the the form YYYY. The window defaults to 1950. */
/* To ensure that the function performs consistently on systems */
/* that support the WINDOW compiler option, that option */
/* must be set to 1950. */
DECLARE D CHARACTER (*);
RETURN (Y4YEAR_2 (D, 1950) );
END Y4YEAR_1;
Y4YEAR_2:
PROCEDURE (D, Window) RETURNS (CHARACTER(4) );
/* INCOMING: D = a two-digit year, lacking the century. */
/* Window = a 4-digit year from which the year will be windowed. */
/* RETURNS: The year in the the form YYYY. */
DECLARE D CHARACTER (*);
DECLARE Window FIXED BINARY;
DECLARE Year FIXED BINARY;
DECLARE Century PICTURE '99';
Year = TRIM(D); /* Extract the year. */
Century = Window/100;
IF Year < MOD(Window, 100) THEN
Century = Century + 1;
RETURN (Century || D);
END Y4YEAR_2;
For further information, contact
email: robin_v@bigpond.com