Seize the greatness

Weekly challenge 237 — 9 October 2023

Week 237 - 9 Oct 2023

Task 1

Given a year, a month, a week of the month, and a day of the week (1 (Mon) .. 7 (Sun)), print the day.

Example 1Input: Year = 2024, Month = 4, Weekday of month = 3, day of week = 2 Output: 16 The 3rd Tue of Apr 2024 is the 16thExample 2Input: Year = 2025, Month = 10, Weekday of month = 2, day of week = 4 Output: 9 The 2nd Thu of Oct 2025 is the 9thExample 3Input: Year = 2026, Month = 8, Weekday of month = 5, day of week = 3 Output: 0 There isn't a 5th Wed in Aug 2026

It would be easiest to do this using one of the date/time modules, but I perversely did it without.

My approach, using as an example the data for Example 1 above, is:

- Get the day of the week of the 1st of January 2024 (see below)
- From that get the day of the week of the 1st of April 2024
- From that get the date of first Tuesday of April 2024
- From that get the date of the 3rd Tuesday

To get the day of the week of the 1st of January 2024 I start with the knowledge that 1 January 1753 (the first full year of the Gregorian Calendar) was a Monday. I then cycle through the years from 1753 to 2024 adding 1 day for each non-leap year (eg Monday → Tuesday) and 2 for every leap year (eg Monday → Wednesday).

The last step is to check whether the day exists - for example the 32nd of April doesn't - and to respond appropriately.

As you will see from my sample output this works for any date from 1753 to 9999 and beyond, up to the largest year number your system can handle, though it will take rather a long time for very large numbers.

You will see that the 5th Friday of December 9999 is the 31st, which will give us the weekend to update all our systems to handle 5-digit years.

#!/usr/bin/perl use v5.16; # The Weekly Challenge - 2023-10-02 use utf8; # Week 237 task 1 - Seize the day use strict; # Peter Campbell Smith use warnings; # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge seize_the_day(2024, 4, 3, 2); seize_the_day(2025, 10, 2, 4); seize_the_day(2026, 8, 5, 3); seize_the_day(1947, 11, 2, 7); seize_the_day(2024, 2, 5, 4); seize_the_day(9999, 12, 5, 5); seize_the_day(1753, 1, 1, 1); seize_the_day(2023, 10, 2, 1); sub seize_the_day { my ($year, $month, $week, $day, @days, @months, @days_in_month, @suffix, $dow, $dom, $result); ($year, $month, $week, $day) = @_; # sanity check say qq[\nInput: \$year = $year, \$month = $month, \$week = $week, \$day = $day]; if ($year < 1753 or $month < 1 or $month > 12 or $week < 0 or $day < 1 or $day > 7) { say qq[Output: bad data]; return; } # initialise @days = qw[Sun Mon Tues Wednes Thurs Fri Satur]; @months = qw[x January February March April May June July August September October November December]; @days_in_month = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); $days_in_month[2] += 1 if is_leap($year); push @suffix, 'th' for (0 .. 31); $suffix[$1] = $2 while '1st 2nd 3rd 21st 22nd 23rd 31st' =~ m|(\d+)([a-z]{2})|g; $day = $day % 7; # get day of the week for 1 Jan $year $dow = -1; $dow += is_leap($_- 1) ? 2 : 1 for 1753 .. $year; # get day of week for 1 $month $year if ($month > 1) { $dow += $days_in_month[$_] for (0 .. $month - 1); } $dow = $dow % 7; # get day of month for first $day in the month $dom = ($day - $dow + 7) % 7 + 1; # get day of month for $week-th $day in the month $dom += ($week - 1) * 7; # does it exist? if ($dom > $days_in_month[$month]) { say qq[Output: There is no $week$suffix[$week] $days[$day]day in $months[$month] $year]; } else { say qq[Output: The $week$suffix[$week] $days[$day]day of $months[$month] $year is the $dom$suffix[$dom]]; } } sub is_leap { # returns 1 for leap year, 0 for non-leap # non-century year : century year return ($_[0] % 100 ? ($_[0] % 4 ? 0 : 1) : ($_[0] % 400 ? 0 : 1)); }

Input: $year = 2024, $month = 4, $week = 3, $day = 2 Output: The 3rd Tuesday of April 2024 is the 16th Input: $year = 2025, $month = 10, $week = 2, $day = 4 Output: The 2nd Thursday of October 2025 is the 9th Input: $year = 2026, $month = 8, $week = 5, $day = 3 Output: There is no 5th Wednesday in August 2026 Input: $year = 1947, $month = 11, $week = 2, $day = 7 Output: The 2nd Sunday of November 1947 is the 9th Input: $year = 2024, $month = 2, $week = 5, $day = 4 Output: The 5th Thursday of February 2024 is the 29th Input: $year = 9999, $month = 12, $week = 5, $day = 5 Output: The 5th Friday of December 9999 is the 31st Input: $year = 1753, $month = 1, $week = 1, $day = 1 Output: The 1st Monday of January 1753 is the 1st Input: $year = 2023, $month = 10, $week = 2, $day = 1 Output: The 2nd Monday of October 2023 is the 9th

The content of
this website
is licensed by
Peter
Campbell Smith under a
Creative Commons Attribution 4.0 International Licence