Harold Zbiegien's date algorithms in REXX
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)