Tabular Programming: Delta Days One of my graphing calculators, an HP 48 series, has a program DDAYS, pronounced ΔDAYS, for reducing two dates to a delta; I occasionally reach for the calculator to use this program, before putting it away again, and thought it would be nice to have a version of such a program on my usual environment with a nicer interface. The HP 48 series of calculators uses a convention for representing dates as numbers, DD.MMYYYY, which is less than pleasant to use. There are other basic improvements to make. Firstly, purely symbolic date manipulation is very simple; following is a trivial example using APL: 2024 02 29-2017 02 02 7 0 27 2017 02 02-2024 02 29 ¯7 0 ¯27 It has been seven years and twenty-seven days since that first article I've written under my domain. Reversing the dates gives the expected inverse answer, and which be preferable is a matter of taste. This can give results such as seven years, minus two months, and twenty-seven days, but that's fine. I'm still ignorant of exactly how many days have passed since I started my writing, however, so it's clear improvements can be made, although the extreme simplicity of the symbolic solution is so nice. After little thought, it seems like a better design still uses both dates as the interface, over use of the first date and a delta, but both designs can be created and compared to confirm my suspicion. For some stupid reason, a leap year occurs every four years, every twenty-fifth leap year is passed, and every hundredth leap year is observed; this APL determines whether a year is a leap year or not: ∇ z←leapyear r z←∨/1 3=2⍴+/0=4 100 400|r ∇ leapyear¨1900 1901 1902 1903 1904 1999 2000 2024 0 0 0 0 1 0 1 1 With that settled, the main problem is solved. It works very simply: Get the modular remains of the year by four, one hundred, and four hundred; see which have remains; and it's a leap year if but one or three do. The nature of these numbers means the third has no remains unless the others do first. I tested this function by applying it to sequences in groups of four hundred, the sequence's period. An alternative approach would be to have a boolean array with four hundred elements, and to map each year to it, but this code is trivially proven to be correct, either way, and so hardly matters here. Months remain the only roadblock in this problem now. That approach I prefer is to have two tables, one representing a normal year and one representing a leap year, which can be combined into a table: 31 28 31 30 31 30 31 31 30 31 30 31 31 29 31 30 31 30 31 31 30 31 30 31 Now the problem is one of alignment: The years will be aligned, followed by the months, and ended by the days; summing the day deltas of each unit in doing so solves this problem. My solution follows: months←2 12⍴31 28 31 30 31 30 31 31 30 31 30 31 31 29 31 30 31 30 31 31 30 31 30 31 adjust←2 12⍴ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 length←+/months ∇ z←leapyear r z←∨/1 3=2⍴+/0=4 100 400|r ∇ ⍝ Years are limited to post 1900, due to the non-contiguous dates at some earlier points in history. ∇ z←l ddays r;⎕IO;y1;y2;m1;m2;d1;d2 ⍝ The second through fourth lines are error checking both dates. ⎕IO←1◊y1←↑l◊y2←↑r◊m1←↑1↓l◊m2←↑1↓r◊d1←↑2↓l◊d2←↑2↓r →bad↓⍨∧/1900≤y1 y2 →bad↓⍨∧/12≥m1 m2 →bad↓⍨months[⎕IO+leapyear y1;m1]≥d1 →bad↓⍨months[⎕IO+leapyear y2;m2]≥d2◊→good bad: ⎕ES 'The parameters described invalid dates.' good: z← (×y1-y2)×+/length[⎕IO+leapyear¨ (y1⌊y2)+⎕IO-⍨⍳|y1-y2] z←z+(×m1-m2)×+/months[⎕IO+leapyear (y1⌈y2);(m2⌊m1)+⎕IO-⍨⍳|m1-m2] z←z-(×y1-y2)×(adjust[⎕IO+leapyear y1⌊y2;])[(m1 m2)[⎕IO+y1>y2]] z←z+(×d1-d2)×|d1-d2 ∇ The adjust table is necessary from the simple realization that two dates are only affected by a leap day if it falls within their range, which it does in one leap year only during January and February. I'd originally written a test for this, before seeing my foolishness and making it the simple table. I'll expand this article at some later time with Common Lisp code, and perhaps even Ada later still. This is a problem not at all suited to APL, and I'm uncertain whether tabular programming principles have helped in any way. I would be unsurprised to learn other implementations to be rather similar, and it still required more thought from me than I wanted to give. Distressingly, writing my program needed trial and error of me, rather than traversal through a path clear-cut by the very approach; I wanted to believe this approach were applicable to everything, but I may have found a simple counter example. Even the process of proving my code to be correct is more arduous than I'd prefer, and I'm left with a feeling that the simple approach I've taken must be correct more than anything rigorous. 2024 02 29 ddays 2017 02 02 2583 It has been two thousand, five hundred, and eighty-three days since that first article I've written. .