Calendar Calculations in J
Vector 13:2, page 117
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 117
Various calculations for the Gregorian calendar are expressed in J, including those for the construction of calendars for a specified month, quarter or year, ordinal and Julian dates, and the number of days between any two dates. Additional date and time verbs may be found in Burke, Hui et al (1996) and in the utility file c:\j3\main\dates.js.
Introduction¶
In the Gregorian calendar, which was introduced in 1582, leap years are defined as those years which are divisible by 4 except for centennial years which must be divisible also by 400. For example, 1996 and 2000 are leap years, but 1993 and 1900 are not. Therefore, a simple test for leap year is given by
where
which has the value 1 if the argument is a leap year, i.e., if the number of the divisors 4, 100 and 400 of its argument is odd, and 0 otherwise.
If we define the constant
then the verb
gives a twelve-item list of the lengths of the months with the second item equal to 29 if the argument is a leap year and 28 otherwise. Also we shall define the list
of day names, and the twelve-item list
of month names.
We shall also define the verbs
for the integer quotient, and
for non-negative integers, where 18 div 7 is 2, and pos 4 is 1 2 3 4.
Date number¶
In order to simplify the calculations we shall consider only dates in the Gregorian calendar beginning with the first day of the seventeenth century, i.e., January 1, 1601, and number the days sequentially with January 1, 1601 as 1, January 2, 1601 as 2, ..., January 1, 1602 as 366, and so on. The sequence number for any date, which we shall call the "date number", may be computed as the sum of the date number of December 31 of the previous year and the ordinal date, i.e., the day number, of the day and month in the given year. For example, the date number for August 1, 1996 is 144484 which is the sum of the date number 144270 for the last day of 1995 and 214, the ordinal date of the given date.
The date number for December 31 of any year is the number of days from January 1, 1601 to December 31 of that year, and is equal to 365 times the number of years plus the number of leap years in the interval. This latter number is the integer quotient of the number of years in the interval divided by 4, minus the integer quotient of this same number divided by 100 to adjust for all centennial years, plus the integer quotient of the number when divided by 400 to account for those centennial years which are leap years. Thus the number of leap years is the alternating sum of the appropriate quotients. Therefore, we may define the verb
which gives the total number of days from January 1, 1601 up to and including December 31 of the year before the given year, and, for example, YearEnd 1996 is 144270. The ordinal date for any day of the year is the sum of the lengths of the preceding months in the year plus the day of the month and is given by
where
gives the number of days in any given month and year. For example, 1 MonthLength 1996 is 31 and 2 MonthLength 1996 is 29, and so on, and OD 1 8 1996 is 214. (The complement of the ordinal date, i.e., the number of days remaining in the year, is given by
and, for example, ODcomp 1 8 1996 is 152.) The date number is then
and D 1 8 1996 is 144484.
Month, quarter and year calendars¶
Let us number the days of the week from 0 to 6, inclusive, starting with Sunday as 0. Since we are numbering the days from January 1, 1601 which was a Monday, the day number for any date is the 7-residue of the date number, and we may define
to give the day number of the first day of any month, and, for example, 8 mb 1996 is 4 which corresponds to Thursday.
The numerical part of a month calendar consists of the consecutive positive integers to an upper limit determined by the month length arranged in a table of six rows since any month except February may extend over six consecutive weeks, and seven columns since there are seven days in the week, and beginning in the first row in a column determined by the first day of the month. This array may be constructed by first generating the appropriate list of positive integers as determined by the month length, converting to a one-column table, formatting to a three-column character table, extending this table to forty-two rows, rotating the rows by an amount given by the day number of the first day of the month, and finally reshaping into the appropriate table. These operations are performed by the verb
where the left and right arguments give the month and year numbers. To obtain the desired year calendar the twelve month calendars must be constructed each with column headings for the names of the days of the week and headed overall with the month name, and finally displayed in one of several possible rectangular arrangements. The verb
gives a labelled month calendar. The dyadic verb
will give month calendars as determined by the left argument for the year given by the right argument. For example, (pos 3 4) cal 1997 gives a 1997 calendar arranged in three rows and four columns as the 2-cells of a table of shape 3 4 8 21.
The verb
will give the month calendars for any quarter of any year, and, for example, 4 Quarter 1996 gives the October, November and December calendars for 1996. The verb
gives a four-by-three calendar for the year specified by the right argument, and has been used to construct the 1997 calendar given in Appendix 1.
Julian Date and its Inverse¶
The Julian date is a system of reckoning used in astronomical calculations for harmonizing the various chronologies used in astronomy. It was proposed in 1582 by the philologist and historian Joseph Scaliger and is defined as the number of days after January 1, 4713 B.C. As an example, the Julian date for August 1, 1996 is 2450297. We shall give verbs to calculate the Julian date corresponding to any calendar date on or after January 1, 1601 — a very simple calculation — and conversely to calculate the calendar date corresponding to any given Julian date — a calculation which appears to be anything but simple.
Since the Julian date for December 31, 1600 is 2305813, the verb
may be used to give the Julian date for a given calendar date, and JD 1 8 1996 is 2450297. The calculation of the inverse Julian date, i.e., the calendar date corresponding to a given Julian date, will be done by finding first the year from the integral number of years from January 1, 1601 represented by the date number corresponding to the Julian date and then the month and day corresponding to the remaining number of days which represent the ordinal date in that year.
We shall first introduce the verb
which may be considered a generalization of the verb div for integer division and which will be used to factor the integer left argument according to the divisors given by the right argument. For example, 75 DIV 20 8 1 is 3 2 4, and 75 is equal to 3 2 4 +/ . * 20 8 1. Now consider the expression
which has the value 0 3 23 3 214, where the left argument is D 1 8 1996, the date number for August 1, 1996, and the first four divisors are, respectively, the number of days in four hundred years, the number of days in one hundred years in which the included centennial year is not a leap year, the number of days in four years, and the number of days in one year which is not a leap year. Therefore, the expression
will give the year 1996 corresponding to the date number, while the last item 214 of the factorization is the ordinal date in that year.
The day and month corresponding to an ordinal date may be found by the verb
where
gives the ordinal calendar for a given year in the form of a 12- item list whose first item gives the ordinal dates for January, i.e., 1, 2, ..., 31, whose second item gives the ordinal dates for February, i.e., 32, 33, ..., 59 or 60, etc. For example, OD 1 8 1996 is 214, and 214 ODinv 1996 is 1 8.
The calculations given in the last two paragraphs may be used to find the calendar date for any date number except for those date numbers corresponding to December 30 or 31 of a leap year when the date is given as ordinal date 0 of the following year.
For example,
has the value 0 3 23 4 0, and 1601 + 400 100 4 1 +/ . * 0 3 23 4 is 1997. The distinguishing feature of these exceptional cases is the number of trailing 0s in factorization of the date number, from one trailing zero in the above example to four for December 31 in a centennial leap year as in
which has the value 1 0 0 0 0. We can resolve this problem using the number of trailing 0s to indicate that an adjustment of the ordinal date and year is necessary. Therefore, we define the verb
where
and, for example,
is 0 214 1996,
is 1 0 1997, and
4 0 2001. We may then define the verb
to adjust the year and ordinal date, and, for example, DayAndYearAdj 30 12 1996 is 365 1996. Then the calendar date for any date number is given by
and, finally, the inverse Julian date by
For example, JDinv 2450297 is 1 8 1996.
Miscellaneous Calculations¶
In this section we shall give a number of very simple and useful calculations which follow immediately from the calculation of either the date number or the Julian date or the inverse of either of these quantities.
It was noted above that the day number corresponding to any date is simply the 7-residue of the date number. Therefore, the day of the week is given by
and, for example, DayOfWeek 1 8 1996 is Th.
The number of days between two dates is the difference in their date numbers so that we may define the verb
and, for example, 1 8 1996 DaysBetween 25 12 1996 is equal to 146. The date a given number of days from any date is given by
and 21 DaysFrom 29 8 1996 is 19 9 1996.
One application of some of these verbs could be to find the approximate date of birth of a litter of kittens given the date that the mother cat conceived. Since the gestation period is approximately 64 days, a verb that will give an estimate of the date of birth is
and, for example, Kittens 1 9 1996 is 4 11 1996.
"A day for any date"¶
There are methods of finding the day of the week for any date which, unlike the method developed in this paper, do not depend upon the calculation of a sequence number such as the Julian date or the date number. One such method is due to Lewis Carroll who published it in the March 31, 1887 issue of Nature. In this section we shall give Carroll’s step-by-step procedure supplemented by the corresponding J expressions which will be gathered together in an explicit verb for performing the calculations. In these expressions the J variables Day, Month and Year will give the values of the day, month and year of the given date. Carroll’s algorithm, as described in Fisher (1975), is as follows with additional commentary added in square brackets:
Take the given date in 4 portions, viz., the number of centuries, the number of years over, the month, the day of the month.
Compute the following 4 items, adding each, when found, to the total of the previous items. When an item or total exceeds 7, divide by 7, and keep the remainder only. [In the J calculations the 7-residue will be found only in the last step.]
The Century-Item: For Old Style (which ended September 2, 1752) subtract from 18. For New Style (which began September 14) divide by 4, take overplus from 3, multiply remainder by 2. [When the Gregorian calendar was introduced in England in 1752, the days between September 2 and September 14 were dropped. In the J expressions calculations for the Julian calendar are not considered.]
The Year-Item: Add together the number of dozens, the overplus, and the number of 4s in the overplus.
The Month-Item: If it begins or ends with a vowel, subtract the number, denoting its place in the year, from 10. This, plus its number of days, gives the item for the following month. The item for January is ‘0’; for February or March (the 3rd month), ‘3’; for December (the 12th month), ‘12’. [Fisher suggests that "it would be surprising if he [Carroll] did not eventually come round to formulating a relevant chart as follows to be committed to memory through a specific mnemonic": 0 3 3 6 8 4 6 2 5 0 3 5. The items represent the 7-residues of the total number of days from January 1 to the beginning of each month.]
The twelve-item list in the above expression may be calculated by
The Day-Item is the day of the month.
The total, thus reached, must be corrected, by deducting ‘1’ (first adding 7, if the total be ‘0’), if the date be January or February in a Leap Year; remembering that every year, divisible by 4, is a Leap Year, excepting only the century-years, in New Style, when the number of centuries is not so divisible (e.g., 1800).
The final result gives the day of the week, ‘0’ meaning Sunday, ‘1’ Monday, and so on.
As an example Carroll uses the date September 18, 1783. Then in the J calculations Day is 18, Month is 9 and Year is 1783 so that c is 2, y is 19, m is 5 and the value of the last expression is 4. Therefore, the given date falls on a Thursday.
We may use the J expressions given above in the definition of the following explicit verb DayNumber:
DayNumber=. 3 : 0
('Day';'Month';'Year')=. y.
c=. +: 3 - mod&4 div&100 Year
y=. ((div&4 + ])@ mod&12 + div&12) mod&100 Year
m=. (<:Month){ 0 3 3 6 8 4 6 2 5 0 3 5
7|c + y + m + Day - LY Year
)
For example, DayNumber 18 9 1783 is 4.
References¶
Burke, C., R. K. W. Hui, et al, 1996. J Phrases. Iverson Software Inc., Toronto.
Fisher, J. (ed.), 1975. The Magic of Lewis Carroll. Penguin Books Ltd., Harmondsworth, Middlesex.
McDonnell, E. E., 1991. ISI & J. Iverson Software Inc., Toronto.
Smillie, Keith, 1992. "Making a calendar in J." Vector, vol. 9, no. 1, pp. 87 - 90.
Smillie, Keith, 1994. "A note on the Easter algorithm in J." Vector, vol. 10, no. 3, pp. 78 - 79.
Appendix 1. Calendar for 1997¶
+---------------------+---------------------+---------------------+
| Jan | Feb | Mar |
| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa|
| 1 2 3 4| 1| 1|
| 5 6 7 8 9 10 11| 2 3 4 5 6 7 8| 2 3 4 5 6 7 8|
| 12 13 14 15 16 17 18| 9 10 11 12 13 14 15| 9 10 11 12 13 14 15|
| 19 20 21 22 23 24 25| 16 17 18 19 20 21 22| 16 17 18 19 20 21 22|
| 26 27 28 29 30 31 | 23 24 25 26 27 28 | 23 24 25 26 27 28 29|
| | | 30 31 |
+---------------------+---------------------+---------------------+
| Apr | May | Jun |
| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa|
| 1 2 3 4 5| 1 2 3| 1 2 3 4 5 6 7|
| 6 7 8 9 10 11 12| 4 5 6 7 8 9 10| 8 9 10 11 12 13 14|
| 13 14 15 16 17 18 19| 11 12 13 14 15 16 17| 15 16 17 18 19 20 21|
| 20 21 22 23 24 25 26| 18 19 20 21 22 23 24| 22 23 24 25 26 27 28|
| 27 28 29 30 | 25 26 27 28 29 30 31| 29 30 |
| | | |
+---------------------+---------------------+---------------------+
| Jul | Aug | Sep |
| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa|
| 1 2 3 4 5| 1 2| 1 2 3 4 5 6|
| 6 7 8 9 10 11 12| 3 4 5 6 7 8 9| 7 8 9 10 11 12 13|
| 13 14 15 16 17 18 19| 10 11 12 13 14 15 16| 14 15 16 17 18 19 20|
| 20 21 22 23 24 25 26| 17 18 19 20 21 22 23| 21 22 23 24 25 26 27|
| 27 28 29 30 31 | 24 25 26 27 28 29 30| 28 29 30 |
| | 31 | |
+---------------------+---------------------+---------------------+
| Oct | Nov | Dec |
| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa| Su Mo Tu We Th Fr Sa|
| 1 2 3 4| 1| 1 2 3 4 5 6|
| 5 6 7 8 9 10 11| 2 3 4 5 6 7 8| 7 8 9 10 11 12 13|
| 12 13 14 15 16 17 18| 9 10 11 12 13 14 15| 14 15 16 17 18 19 20|
| 19 20 21 22 23 24 25| 16 17 18 19 20 21 22| 21 22 23 24 25 26 27|
| 26 27 28 29 30 31 | 23 24 25 26 27 28 29| 28 29 30 31 |
| | 30 | |
+---------------------+---------------------+---------------------+
Appendix 2. Script file¶
NB. Introduction
LY=. ~:/ @ (mod&4 100 400 = 0:)
mod=. |~
LENGTHS=. 31 28 31 30 31 30 31 31 30 31 30 31
L=. LENGTHS&+@((LENGTHS=28)&*.)@LY
DAYS=. ' Su Mo Tu We Th Fr Sa'
MONTHS=._3<\'JanFebMarAprMayJunJulAugSepOctNovDec'
div=.<.@%
pos=. >: @ i.
NB. Date number
YearEnd=. (365&* + -/ @ (div&4 100 400)) @ (] @ -&1601)
MonthLength=. (<:@[) { L@]
OD=. ({.@])++/ @ (pos @ (<: @ (1&{@])) MonthLength"(0 _) {:@])
ODcomp=. LY@({:@]) + 365&-@OD
D=. (YearEnd@({:)@]) + OD
NB. Month, quarter and year calendars
mb=. mod&7 @ (D @ (1:,[,]))
mctable=. ,"_1@(6 7&$)@((-@mb) |.
(42&{.)@(3&":)@,.@pos@MonthLength)
mc=. (_12&{.@>@({&MONTHS)@<:@[),(DAYS&,@mctable)
cal=. <"2 @ mc"(0 _)
Quarter=. ((pos 3)&+@((3: * <:)@[))cal]
calendar=. (pos 4 3)&cal
NB. Julian date and its inverse
JD=. 2305813&+@D
ODcal=. (+/\@(0:,}:) (([+pos@])"(0 0) &. >) ])@L
ODinv=. |.@>:@( 12 31&#:)@(,@(>@ODcal@]) i. [)
DIV=. (}.@[)`((div {.),(mod {.) DIV }.@])@.(*@#@])
Year=. 1601&+@(400 100 4 1&(+/ . *))
DayAndYear=. (((0:<|.)i.1:),{:,Year@}:)@(DIV&146097 36524 1461
365 1)
DayAndYearAdj=. (}.)`((366&-@(2&|@{.) ,
<:@{:))@.(0:~:{.)@DayAndYear
Dinv=. (({.ODinv(1&{)) , }.)@DayAndYearAdj
JDinv=. Dinv@(-&2305813)
NB. Miscellaneous calculations
DayOfWeek=.;@((_3<\DAYS)&({~)@(mod&7 @D))
DaysBetween=. D@[ -~ D@]
DaysFrom=. Dinv@([+D@])
Kittens=. 64&DaysFrom
NB. "A day for any date"
DayNumber=. 3 : 0
('Day';'Month';'Year')=. y.
c=. +: 3 - mod&4 div&100 Year
y=. ((div&4 + ])@ mod&12 + div&12) mod&100 Year
m=. (<:Month){ 0 3 3 6 8 4 6 2 5 0 3 5
7|c + y + m + Day - LY Year
)