Harold Zbiegien's date algorithms in REXX

From EDM2
Jump to: navigation, search

Harold Zbiegien of the American Greetings Corp. created a rather nifty set of algorithms to calculate days of the week a few years back, it is based on a number of older algorithms, notably one published by Richard A. Stone in 1970. While there are better alternatives available out there especially for historical dates and for incorporating calendars other than the Gregorian one currently used in the west, Zbiegien's version of the algorithm is much simpler than most and has been tested correct for dates from October 15th 1582 to 31 December 9999 with the exception of as yet undecided "dropped days" that will happen between the years 4317 and 9000.

Zbiegien's original implementation was in Fortran, but Michel Castelein created a REXX version that is shown below. Note it is reproduced verbatim and is formatted for MVS REXX but most if not all work on OS/2 REXX without modification.

Please note that the code here below is copyrighted and not under the Attribution-Share Alike 3.0 licence like the rest of the EDM/2 Wiki.

/* REXX exec CONVERT1
Converts "YYYYMMDD" into (1) Julian date "YYYYNNN" (NNN is 001-366)
and (2) day-of-week.
(1): Algorithm 398 in the Oct 1970 Communication of the ACM,
by Richard A. Stone.
(2): added by Harold Zbiegien.
*/
arg YYYY +4 MM +2 DD
if YYYY // 4 = 0 then LY = 1 ; else LY = 0
if YYYY // 100 = 0 then LY = 0
if YYYY // 400 = 0 then LY = 1 /* LY is 1 if it is a leap year
*/
NNN = TRUNC(((MM + 2) * 3055) / 100) + DD - 91
if NNN > (59 + LY) then NNN = NNN - 2 + LY
NNN = RIGHT(NNN,3,0)
T = TRUNC(YYYY / 100) - 6 - TRUNC(YYYY / 400)
DOW = (NNN + TRUNC((YYYY * 5) / 4) - LY - T) // 7
/* DOW is 0-6, 0=Sunday, 1=Monday, etc.)
*/
array = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'
day_of_week = WORD(array,DOW+1)
say YYYY || MM || DD '=' YYYY || NNN || ',' day_of_week


/* REXX exec CONVERT2
Converts Julian date "YYYYNNN" into (1) "YYYYMMDD"
and (2) day-of-week.
This is a reversal of routine CONVERT1, worked out by Harold
Zbiegien

*/
arg YYYY +4 NNN
if YYYY // 4 = 0 then LY = 1 ; else LY = 0
if YYYY // 100 = 0 then LY = 0
if YYYY // 400 = 0 then LY = 1 /* LY is 1 if it is a leap year
*/
WORK = NNN
if WORK > (59 + LY) then WORK = WORK + 2 - LY
MM = TRUNC(((WORK + 91) * 100) / 3055)
DD = (WORK + 91) - TRUNC((MM * 3055) / 100)
DD = RIGHT(DD,2,0)
MM = MM - 2
MM = RIGHT(MM,2,0)
T = TRUNC(YYYY / 100) - 6 - TRUNC(YYYY / 400)
DOW = (NNN + TRUNC((YYYY * 5) / 4) - LY - T) // 7
/* DOW is 0-6, 0=Sunday, 1=Monday, etc.)
*/
array = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'
day_of_week = WORD(array,DOW+1)
say YYYY || NNN '=' YYYY || MM || DD || ',' day_of_week


/* REXX exec CONVERT3
Converts Julian date "YYYYNNN" into Lilian integer date (9 digits).
Cf "Computer processing of dates outside the twentieth century"
by B. G. Ohms, IBM Systems Journal, volume 25 no 2, 1986.
*/
arg YYYY +4 NNN
LIL = TRUNC(((YYYY - 1201) * 36525) / 100) - 139444 + NNN ,
- TRUNC((YYYY - 1201) / 100) ,
+ TRUNC((YYYY - 1201) / 400)
LIL = RIGHT(LIL,9,0)
say YYYY || NNN '= Lilian date' LIL


/* REXX exec CONVERT4
Converts Lilian integer date into Julian date "YYYYNNN".
Cf "Computer processing of dates outside the twentieth century"
by B. G. Ohms, IBM Systems Journal, volume 25 no 2, 1986.
*/
arg LIL
CLD = TRUNC(((LIL + 139444) * 100) / 3652425)
NNN = CLD + LIL + 139444 - TRUNC(CLD / 4)
WORK = TRUNC((NNN * 100) / 36525)
IF (NNN * 100) // 36525 = 0 then WORK = WORK - 1
NNN = NNN - TRUNC((WORK * 36525) / 100)
NNN = RIGHT(NNN,3,0)
YYYY = WORK + 1201
say 'Lilian date' RIGHT(LIL,9,0) '=' YYYY || NNN


/* REXX exec CONVERT5
Converts Lilian integer date into day-of-week.
By Harold Zbiegien (E-mail bb206@...).
*/
arg LIL
DOW = (LIL + 4) // 7 /* DOW is 0-6, 0=Sunday, 1=Monday, etc.)
*/
array = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'
day_of_week = WORD(array,DOW+1)
say 'Lilian date' RIGHT(LIL,9,0) '=' day_of_week


/* REXX exec CONVERT6
Converts "YYYYMMDD" into Julian date "YYYYNNN".
Cf "The Year 2000 Computing Crisis" by J.T. and M.J. Murray.
*/
arg YYYY +4 MM +2 DD
if YYYY // 4 = 0 then LY = 1 ; else LY = 0
if YYYY // 100 = 0 then LY = 0
if YYYY // 400 = 0 then LY = 1 /* LY is 1 if it is a leap year
*/
NNN = TRUNC(3 / (MM + 1)) * (31 * (MM - 1) + DD) ,
+ TRUNC((MM + 9) / 12) * (TRUNC(((305 * (MM - 1) - 15) ,
+ TRUNC((MM + 3) / 12) * 5 * TRUNC(18 / MM)) / 10) + DD + LY)
NNN = RIGHT(NNN,3,0)
say 'YYYYMMDD' YYYY || MM || DD '= YYYYNNN' YYYY || NNN


/* REXX exec CONVERT7
Converts "YYYYMMDD" into day-of-week.
This is a Zeller's congruence algorithm submitted by Mike Carroll
to the bit.listserv.ibm-main group on 5 May 1995.
*/
arg YYYY +4 MM +2 DD
if MM < 3 then do ; WMM = MM + 12
WYYYY = YYYY - 1
end
else do ; WMM = MM
WYYYY = YYYY
end
DOW = (DD + 1 + (WMM * 2) + TRUNC(((WMM + 1) * 3) / 5) ,
+ WYYYY + TRUNC(WYYYY / 4) - TRUNC(WYYYY / 100) ,
+ TRUNC(WYYYY / 400)) // 7
/* DOW is 0-6, 0=Sunday, 1=Monday, etc.)
*/
array = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'
say 'YYYYMMDD' YYYY || MM || DD '=' WORD(array,DOW+1)

Authors

  • Richard A. Stone
  • B. G. Ohms
  • Mike Carroll
  • J. T. Murray
  • M. J. Murray
  • Harold Zbiegien
  • Michel Castelein (REXX version)