Calendar science

I remember - 20+ years ago or so - trying to workaround some exclusive cronjob.

e.g. ONLY run this shit, if its the 2nd or 3rd Sunday of the month…

That was on Solaris. Back then - google (or whatever search engine you used back then) didn’t yield much fruit… I gave up…

I’m sure Perl could do something - but I never learned Perl, still haven’t used it “in anger” (I know the barest essentials, so that I can edit a few variables in a perl script - I came across something recently - that was 10+ years old, and someone had essentially created some kinda pseudo init that used perl to run stuff in RHEL 5, inerdeptendant perl scripts that tried to run as daemons, and kill itself if it hung and re-launch, it was truly breathtaking in its complexity and I was immensely relieved when I was told I didn’t have to reverse engineer its logic - PHEW! : essentially I think they wanted to reverse engineer it to do SFTP instead of FTP - FFS - SFTP was a “thing” in 2012/13 when they deployed this VAST mess! And they didn’t even use “native” FTP client you get with Linux - they used some alternative called “lftp” FFS! )

There is worse.
We used to mate our experimental sheep on the first full week after Easter.
That involves the Lunar calendar as well as the Gregorian calendar, plus the interaction with Anzac Day.
Noone ever tried to computerize it.

3 Likes

Trafalgar day for us!!

1 Like

Check that 12. I only get the following

> cesunday
function(y)
# cesunday() Years in which Christmas Eve is on a Sunday
{
  x <- vector(length=0)
  j <- 0
  for(i in 1:length(y)) {
   if(dayofweek(y[i],12,24) == 0) {
    j <- j + 1
    x[j] <- y[i]
   }
  }
  return(x)
}

slightly modified to return a vector

cedat <- cesunday(seq(1800,4000))
hist(diff(cedat))

and the histogram is
diffcedat

So I only get 5,6, and 11.
The 6’s are more frequent

Definitely no 12’s
Here is the data from which I drew the histogram

> diff(cedat)
  [1]  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5
 [26]  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5
 [51]  6 11  6 11  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6
 [76] 11  6 11  6  5  6  6  5  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6
[101] 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6 11  6 11  6  5  6  6  5  6 11
[126]  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6  5  6  6  5  6 11
[151]  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6 11  6
[176] 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6
[201] 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6
[226] 11  6 11  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11
[251]  6 11  6  5  6  6  5  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11
[276]  6 11  6  5  6  6  5  6 11  6 11  6  5  6 11  6 11  6  5  6  6  5  6 11  6
[301] 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6  5

There are 7 integers between 5 and 11, but only 3 of them occur .

2 Likes

That is the number of years from one Christmas Eve Sunday to the next.

Did you notice that… there is a simple repeating pattern
A set of 9 numbers which repeats forever.
No not quite
Every seventh set of nine, the four adjoining values 6,11,6,11 is replaced by two 6,11

6  5
 [26]  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5
 [51]  6 11  6 11  6  5  6 11  6 11  6  5  6  6  5  6 11  6 11  6  5  6  6  5  6
 [76] 11  6 11  6  5  6  6  5  6 11  6  5

I wonder if there are other irregularities of a longer period?

Can we explain that?

There is a basic pattern of 9 integers, which repeats, except that at every
seventh repeat the first 2 values are omitted?

Patterns are interesting.

2 Likes

I think I came up with those from the Powershell version. I just came up with the same using the original Python version.

The 12 is from 2102 - 2090. What years do you see in that timeframe?

The time and date website agrees that those dates are on a Sunday.

1 Like

My full list of years

> cedat
  [1] 1803 1809 1820 1826 1831 1837 1843 1848 1854 1865 1871 1882 1888 1893 1899
 [16] 1905 1910 1916 1927 1933 1944 1950 1955 1961 1967 1972 1978 1989 1995 2000
 [31] 2006 2012 2017 2023 2034 2040 2051 2057 2062 2068 2074 2079 2085 2096 2102
 [46] 2113 2119 2124 2130 2136 2141 2147 2158 2164 2175 2181 2186 2192 2203 2209
 [61] 2220 2226 2231 2237 2243 2248 2254 2265 2271 2282 2288 2293 2299 2305 2310
 [76] 2316 2327 2333 2344 2350 2355 2361 2367 2372 2378 2389 2395 2400 2406 2412
 [91] 2417 2423 2434 2440 2451 2457 2462 2468 2474 2479 2485 2496 2502 2513 2519
[106] 2524 2530 2536 2541 2547 2558 2564 2575 2581 2586 2592 2603 2609 2620 2626
[121] 2631 2637 2643 2648 2654 2665 2671 2682 2688 2693 2699 2705 2710 2716 2727
[136] 2733 2744 2750 2755 2761 2767 2772 2778 2789 2795 2800 2806 2812 2817 2823
[151] 2834 2840 2851 2857 2862 2868 2874 2879 2885 2896 2902 2913 2919 2924 2930
[166] 2936 2941 2947 2958 2964 2975 2981 2986 2992 3003 3009 3020 3026 3031 3037
[181] 3043 3048 3054 3065 3071 3082 3088 3093 3099 3105 3110 3116 3127 3133 3144
[196] 3150 3155 3161 3167 3172 3178 3189 3195 3200 3206 3212 3217 3223 3234 3240
[211] 3251 3257 3262 3268 3274 3279 3285 3296 3302 3313 3319 3324 3330 3336 3341
[226] 3347 3358 3364 3375 3381 3386 3392 3403 3409 3420 3426 3431 3437 3443 3448
[241] 3454 3465 3471 3482 3488 3493 3499 3505 3510 3516 3527 3533 3544 3550 3555
[256] 3561 3567 3572 3578 3589 3595 3600 3606 3612 3617 3623 3634 3640 3651 3657
[271] 3662 3668 3674 3679 3685 3696 3702 3713 3719 3724 3730 3736 3741 3747 3758
[286] 3764 3775 3781 3786 3792 3803 3809 3820 3826 3831 3837 3843 3848 3854 3865
[301] 3871 3882 3888 3893 3899 3905 3910 3916 3927 3933 3944 3950 3955 3961 3967
[316] 3972 3978 3989 3995 4000

They are different… I dont have 2090
So one of us is wrong… probably my version of Sakomoto’s algorithm
but it is just possible that your online calendar is wrong

I checked with R’s internal function

> weekdays(as.Date("2096-12-24"))
[1] "Monday"

So it looks like my version is wrong for 2096.
Can I get a list of years from your code to see exactly where they deviate?

Reference

It is also in Wikipedia

2 Likes

I have the answer… my R code for Sakamoto’s algorithm in reply #17
is wrong.
The C code in Wikipedia is at least sloppy if not incorrect

dayofweek(y, m, d)	/* 1 <= m <= 12,  y > 1752 (in the U.K.) */
{
    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    if ( m < 3 )
    {
        y -= 1;
    }
    return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}

That is supposed to be C code… but the / for divide in the second last line
is an integer divide. When C divides 2 integers, it gives an integer result…
but nowhere is y defined as int.

In R it is different. There is a special operator for integer divide %/%
if you use ‘/’ you get a floating point result. So my R code should be

 function(y, m, d )
# dayofweek(y,m,d)
# Sakamoto's algorithm for day of week in Gregorian calendar
# 1 <= m <= 12 , y > 1752 (in UK)
# returns 0=Sunday, 1=Monday, ...
{
  t <- c(0,3,2,5,0,3,5,1,4,6,2,4)
  if (m < 3) {
    y <- y - 1
  }
  return ((y + y %/% 4 - y %/% 100 + y %/% 400 + t[m] + d) %% 7)
}

When I use this revised routine I get

> cedat <- cesunday(seq(1800,4000))
> hist(diff(cedat))

diffcedat2
So now there are a small number of 12’s
The data now look looe this

    Year Years to next
1   1809             6
2   1815             5
3   1820             6
4   1826            11
5   1837             6
6   1843             5
7   1848             6
8   1854            11
9   1865             6
10  1871             5
11  1876             6
12  1882            11
13  1893             6
14  1899             6
15  1905             6
16  1911             5
17  1916             6
18  1922            11
19  1933             6
20  1939             5
21  1944             6
22  1950            11
23  1961             6
24  1967             5
25  1972             6
26  1978            11
27  1989             6
28  1995             5
29  2000             6
30  2006            11
31  2017             6
32  2023             5
33  2028             6
34  2034            11
35  2045             6
36  2051             5
37  2056             6
38  2062            11
39  2073             6
40  2079             5
41  2084             6
42  2090            12
43  2102            11
44  2113             6
45  2119             5
46  2124             6
47  2130            11
48  2141             6
49  2147             5
50  2152             6
51  2158            11
.......

Still a repeating pattern , but the 12’s are quite irregularly spaced

> cedat.df[cedat.df[,2] == 12, ]
    Year Years to next
42  2090            12
56  2197            12
98  2490            12
112 2597            12
154 2890            12
168 2997            12
210 3290            12
224 3397            12
266 3690            12
280 3797            12

There is a pattern… it is always x90 and (x+1)97 … and they occur at 400
year intervals… so I guess the period of repetition of the pattern might be 400 years.

Thanks @pdecker for sorting me out.

3 Likes

I just kind of came to that conclusion myself while reading your list. I had not really looked for a pattern with 6 5 6 11. I noticed the exceptions to that pattern were around years when there was a leap year exception due to being divisible by 100. There wasn’t a disruption near the year 2000 because that was also divisible by 400 and was thus a leap year like it would normally be, being divisible by 4.

What a couple of geeks we are.

1 Like

Well algorithms are a bit geeky.
There is even a one-line algorithm for calculating day of week.
It is called Keith’s algorithm… see here

It is in C, and I am not going to try and translate it to R, lets do it in C

#include	<stdio.h>
#include	<stdlib.h>

/* keith.c    calculate day of week */

main()
{
  int y,m,d,day;
   while (scanf("%i %i %i", &y,&m,&d) == 3) {
    printf("%i %i %i \n",y,m,d);
     day = (d+=m<3 ? y-- : y-2,23*m/9+d+4+y/4-y/100+y/400) % 7 ;
     printf("%i %i %i %i \n",y,m,d,day);
   }
  exit(0);
}

We can compile it with
gcc -o keith keith.c
I then setup a script to run it

#!/bin/sh
 keith <<eoi
  2023 12 24
  2024 12 24
  2025 12 24
  2026 12 24
  2027 12 24
  2028 12 24
 eoi

and when I run the script I get

$ ./keith.sh
2023 12 24 
2023 12 2045 0 
2024 12 24 
2024 12 2046 2 
2025 12 24 
2025 12 2047 3 
2026 12 24 
2026 12 2048 4 
2027 12 24 
2027 12 2049 5 
2028 12 24 
2028 12 2050 0 

It seems right, 2023 and 2028 are Sundays (0)
Note that it alters the value of d

While I appreciate the cleverness of shoehorning that calculation in to
one line of code, I think most would agree Sakamoto’s algorithm is
more elegant and more understandable. Here it is as a C function
for com[arison

dayofweek(y, m, d)	/* 1 <= m <= 12,  y > 1752 (in the U.K.) */
{
    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    if ( m < 3 )
    {
        y -= 1;
    }
    return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}

That integer expression
y/4 - y/100 + y/400
occurs in both.
What that expression does is put in the extra days provided by leap years.
The whole expression calculates total number of days (since Jan 1 in a year in which Jan1 was a sunday) then uses remainder on division by 7, to get day of week

3 Likes

Hi @KrunalRana ,
I like the shell script… It is all laid out in an understandable
manner.
Have you tested it?

Cant comment on python… do not use it.

Regards
Neville