💾 Archived View for thrig.me › blog › 2023 › 12 › 28 › monday-the.gmi captured on 2024-03-21 at 15:12:53. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-12-28)

-=-=-=-=-=-=-

Monday the 25th

Someone was commenting on Monday the 25th and recollecting that it did not happen much. I had never much noticed the particular day, as in IT one will often end up working the 25th or 1st because invariably something breaks and someone must be on-call. One year it turned out that the programmers had used Pike, which broke across a new year, because date time code does that. What to do? Dive in and attempt to fix an unknown programming language on a newly discovered system? Try to drag in drunk, sleeping, or otherwise unavailable programmers to the party? Collect more information but mostly sit it out and hope that the error isn't too bad and does not impact too many systems?

My recollection was that I had some Julian date code copied from some celestial calculations book and that there had been a "what day of the week?" bit of code shown, so finding Monday the 25ths is not too difficult.

    (defun frac (x) (abs (- x (truncate x))))

    (defun to-julian (year month day)
      (declare (inline frac))
      (when (< year 1582) (error "too early for gregorian"))
      (unless (> month 2) (psetf year (1- year) month (+ 12 month)))
      (let* ((ttt (if (minusp year) 0.75d0 0))
             (aaa (truncate (/ year 100)))
             (bbb (+ (- 2 aaa) (truncate (/ aaa 4)))))
        (+ bbb
           (truncate (- (* 365.25d0 year) ttt))
           (truncate (* 30.6001d0 (1+ month)))
           day
           1720994.5d0)))

    (defun day-of-week (year month day)
      (declare (inline to-julian))
      (round (* 7d0 (frac (/ (+ (to-julian year month day) 1.5d0) 7d0)))))

    (loop for year from 1582 to 2038 do
          (when (eq 1 (day-of-week year 12 25))
            (format t "~&~a~&" year)))

We stop at 2038 to invite specious 32-bit time_t comments, when some unix systems will rollover and maybe play dead. Anyways, a list of years.

    1589 1595 1600 1606 1617 1623 1628 1634 1645 1651 1656 1662 1673 1679
    1684 1690 1702 1713 1719 1724 1730 1741 1747 1752 1758 1769 1775 1780
    1786 1797 1809 1815 1820 1826 1837 1843 1848 1854 1865 1871 1876 1882
    1893 1899 1905 1911 1916 1922 1933 1939 1944 1950 1961 1967 1972 1978
    1989 1995 2000 2006 2017 2023 2028 2034

Some of these years may be invalid, as we're going from Gregorian to Julian, and there were some, um, politics around that transition. A typical unix system uses the English (or American) date system, which only donned Gregorian in 1752, so Decembers prior to that will fail cross checks (unless the calendars accidentally are in sync that year).

    $ cal 9 1752
       September 1752
    Su Mo Tu We Th Fr Sa
           1  2 14 15 16
    17 18 19 20 21 22 23
    24 25 26 27 28 29 30



    $ cal 12 1752
       December 1752
    Su Mo Tu We Th Fr Sa
                    1  2
     3  4  5  6  7  8  9
    10 11 12 13 14 15 16
    17 18 19 20 21 22 23
    24 25 26 27 28 29 30
    31
    $ cal 12 1747
       December 1747
    Su Mo Tu We Th Fr Sa
           1  2  3  4  5
     6  7  8  9 10 11 12
    13 14 15 16 17 18 19
    20 21 22 23 24 25 26
    27 28 29 30 31

You are double checking and testing your date time code against other things, right? Too many programmers do not, and then hilarity happens, like when code at Microsoft tried to use February 29th a year later and whoops there is no such date and whoops there go the signing certificates and whoops there went Azure. Too many other such examples can be found.

There is something of a pattern to the delta years between Monday the 25ths. Sometimes the gap goes up to 12 years, when it's not doing the more typical 11 6 5 6 thing.

    6 5 6 11 6 5 6 11 6 5 6 11 6 5 6 12 11 6 5 6 11 6 5 6 11 6 5 6 11 12 6 5
    6 11 6 5 6 11 6 5 6 11 6 6 6 5 6 11 6 5 6 11 6 5 6 11 6 5 6 11 6 5 6

Probably you'll want to watch out for that 6 6 6 sequence. Arnold Schoenberg was notably superstitious about numbers.

And yet another opinion. It takes more CPU than the LISP code run under SBCL does, but probably has more tests than my bespoke code does.

    #!/usr/bin/perl
    use 5.36.0;
    use DateTime;
    for my $yyyy ( 1582 .. 2038 ) {
        my $dt = DateTime->new( year => $yyyy, month => 12, day => 25 );
        say $yyyy if $dt->day_of_week == 1;
    }