The Last Monday

JavaScript (Firefox 30+), 112 109 103 95 bytes

Look ma, no built-ins!

y=>[for(m of(i=0,y%4|y%400*!(y%100)&&6)+"63153042641")if((i++,y+(y>>2)-(y/100|0)*3/4|0)%7==m)i]

Here's a 107-byte ES6 version:

y=>[...(y%4|y%400*!(y%100)&&6)+"63153042641"].map((m,i)=>(y+(y>>2)-(y/100|0)*3/4|0)%7-m?0:i+1).filter(x=>x)

And here's my previous attempt, 123 113 bytes of ES6:

y=>[(l=y%4|y%400*!(y%100))?[7]:[1,7],[4,12],[9],[3,6],[8,11],[5],l?[1,2,10]:[2,10]][(y+(y>>2)-(y/100|0)*3/4|0)%7]

Explanation

The day of the week of a particular year is calculated like so:

y+(y>>2)-(y/100|0)*3/4|0)%7

In other words:

  • Take y.
  • Add the number of 4th years before y (y>>2).
  • Subtract the number of 100th years before y (y/100|0).
  • Add back in the number of 400th years before y; this is 1/4 of y/100|0, so we use *3/4|0.

Then we modulo the result by 7. If we let 0 mean Sunday, 1 mean Monday, etc., the result corresponds to the day of the week of December 31st of that year. Therefore, for December, we want to check if the result is 1. This gives us the last char in the string.

The last day of November is 31 days before the last day of December. This means that for the last day of November to be a Monday, Dec 31 needs to be a (1 + 31) % 7 = 4 = Thursday.

This procedure is repeated until we get back to March (a 3). Whether or not there is a leap day, the last day of February is 31 days before the last day of March, so we can calculate that too (it's (3 + 31) % 7 = 6). The tricky part is finding the correct value for January:

  • If it is a leap year, the last day of January is 29 days before the last day of Feb, resulting in (6 + 29) % 7 = 0.
  • Otherwise, it is 28 days before the last day of Feb, resulting in (6 + 28) % 7 = 6.

We can calculate whether or not it is a leap year with the following snippet:

!(y%400)|y%100*!(y%4)

This gives 0 if y is not a leap year, and a positive integer otherwise. This leads us to

!(y%400)|y%100*!(y%4)?0:6

for calculating the day for January. However, we can do better by reversing the conditions:

y%4|y%400*!(y%100)?6:0

Since the falsy result is always 0 anyway, we can reduce it to

y%4|y%400*!(y%100)&&6

saving one more precious byte.

Putting it all together, we loop through each char in the string, checking if each is equal to the day of the week of Dec 31st. We keep the indexes of the ones that match, returning this array in the end. And that is how you do leap year calculations without built-ins.


JavaScript (Firefox 30-57), 67 65 64 63 61 bytes

y=>[for(_ of(m='')+1e11)if(new Date(y+400,++m).getDay()==2)m]

Saved 2 4 6 bytes thanks to @ETHproductions. Saved another byte by outputting the months in reverse order.


MySQL, 183 134 129 106 bytes

SET @y=2016;SELECT help_topic_id AS m FROM mysql.help_topic HAVING m BETWEEN 1 AND 12 AND 2=DAYOFWEEK(LAST_DAY(CONCAT(@y,-m,-1)))

Replace 2016 with the desired year. Run.

Rev. 2: Used the help_topics table in the default installation instead of creating a temporary table.

Rev.3: Adopted aross´s - trick and noticed I can also omit the quotes for "-1".
However, -1 is required in MySQL: I need a full date.

Rev.4: Restriction m BETWEEN 1 AND 12 could be done as m>0 AND m<13 (-6), but is not needed at all - invalid values will be ignored; warnings will be counted but not listed.

Tags:

Date

Code Golf