The weekly challenge 237 - Task 1: Seize The Day

 1 #!/usr/bin/perl
 2 # https://theweeklychallenge.org/blog/perl-weekly-challenge-237/#TASK1
 3 #
 4 # Task 1: Seize The Day
 5 # =====================
 6 #
 7 # Given a year, a month, a weekday of month, and a day of week (1 (Mon) .. 7 (Sun)), print the day.
 8 #
 9 ## Example 1
10 ##
11 ## Input: Year = 2024, Month = 4, Weekday of month = 3, day of week = 2
12 ## Output: 16
13 ##
14 ## The 3rd Tue of Apr 2024 is the 16th
15 #
16 ## Example 2
17 ##
18 ## Input: Year = 2025, Month = 10, Weekday of month = 2, day of week = 4
19 ## Output: 9
20 ##
21 ## The 2nd Thu of Oct 2025 is the 9th
22 #
23 ## Example 3
24 ##
25 ## Input: Year = 2026, Month = 8, Weekday of month = 5, day of week = 3
26 ## Output: 0
27 ##
28 ## There isn't a 5th Wed in Aug 2026
29 #
30 ############################################################
31 ##
32 ## discussion
33 ##
34 ############################################################
35 #
36 # This is a case for the DateTime module:
37 # - First we calculate the day of week for the 1st of the given month by
38 #   creating a DateTime object
39 # - Then we calculate the first day of the month that matches our target
40 #   day of week
41 # - From that, we calculate the weekday of month's date by adding another
42 #   7 for each week we're out from the first week
43 # - If we went past the end of the month, we return 0, otherwise the
44 #   calculated date
45 
46 use strict;
47 use warnings;
48 use DateTime;
49 
50 
51 seize_the_day(2024, 4, 3, 2);
52 seize_the_day(2025, 10, 2, 4);
53 seize_the_day(2026, 8, 5, 3);
54 
55 sub seize_the_day {
56    my ($year, $month, $weekday_of_month, $day_of_week) = @_;
57    print "Input: Year = $year, month = $month, weekday of month = $weekday_of_month, day of week = $day_of_week\n";
58    if($weekday_of_month < 1 or $weekday_of_month > 5) {
59       print "Output: 0\n";
60       return;
61    }
62    my $dt = DateTime->new( year => $year, month => $month, day => 1 );
63    my $days_per_month = {
64       1 => 31, 2 => $dt->is_leap_year ? 29 : 28,
65       3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31,
66       8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31
67    };
68    my $dow_1st = $dt->day_of_week;
69    my $first_appearance_of_dow = $day_of_week >= $dow_1st ? ( $day_of_week - $dow_1st + 1 ) : ( $day_of_week - $dow_1st + 8);
70    my $nth_appearance_of_dow = $first_appearance_of_dow + ($weekday_of_month-1) * 7;
71    if($nth_appearance_of_dow > $days_per_month->{$month}) {
72       print "Output: 0\n";
73       return;
74    }
75    print "Output: $nth_appearance_of_dow\n";
76 }
77