ETOOBUSY 🚀 minimal blogging for the impatient
PWC138 - Workdays
TL;DR
Here we are with TASK #1 from The Weekly Challenge #138. Enjoy!
The challenge
You are given a year,
$year
in 4-digits form.Write a script to calculate the total number of workdays in the given year.
For the task, we consider, Monday - Friday as workdays.
Example 1
Input: $year = 2021 Output: 261
Example 2
Input: $year = 2020 Output: 262
The questions
I guess that the laconic assertion about what workdays are put an end to all questions: there’s no holidays, vacations, or other fancy stuff. Just plain weeks with 5 workdays each. Right?!?
I’d probably ask if we can stick to dates in the Gregorian Calendar but I won’t wait for the answer and assume it’s a yes.
The solution
I already said that Mohammad Sajid Anwar is amazing and this challenge shows how kind he is: we can reuse a lot from the previous week!
We can look at the year by taking into consideration three parts:
- the first week, considering it from the first day of the year up to and including the first Sunday;
- the last week, considering it from the last Monday up to the end of the year
- the rest of the year, which will by definition be composed by complete weeks only.
We will stick to the convention that Monday is 1 up to Sunday that is 7. If the first day of the week is $m$, then the first week:
- will contain $n = 8 - m$ days, and
- will contain $max(0, n - 2)$ work days.
Not convinced about that weird $max(\cdot)$? The first week wil always contain a Sunday, and possibly a Saturday, so whatever number of workdays will always have to exclude these two days. When we calculate $n - 2$, if we get a negative number then there are surely no workdays, so we use $max(\cdot)$ to clamp the value to $0$. It’s a math trick.
Similarly, the last week will have a number of days corresponding to the number of the last day. In this case, though, it will always start from Monday and increase with workdays up to a maximum of $5$. So, if the number of days in the week is $k$, the number of workdays in that week will be $min(k, 5)$, underlining that there’s a cap to 5 workdays in the week.
Last, the core of the year will have a number $d$ of says that is a multiple of $7$. Out of them, exactly $5$ are workdays… so the computation is easy: $\frac{7}{5}d$.
Enough talking, let’s get to the Raku code:
#!/usr/bin/env raku
use v6;
subset FullyGregorianYear of Int where * > 1582;
sub workdays (FullyGregorianYear $y) {
my $bdow = Date.new($y, 1, 1).day-of-week;
my $edow = Date.new($y, 12, 31).day-of-week;
my $bdays = 8 - $bdow; # 1 - 7
my $ydays = 365 + ($bdow == $edow ?? 0 !! 1) - $bdays - $edow;
return max($bdays - 2, 0) + ($ydays / 7 * 5).Int + min($edow, 5);
}
sub MAIN (FullyGregorianYear $y = 2021) { workdays($y).put }
Time for Perl now:
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use Time::Local 'timegm';
use List::Util qw< min max >;
sub dow ($y, $m, $d) { (gmtime(timegm(1, 1, 1, $d, --$m, $y)))[6] }
sub workdays ($y) {
my $bdow = dow($y, 1, 1);
my $edow = dow($y, 12, 31);
my $bdays = 8 - $bdow; # 1 - 7
my $ydays = 365 + ($bdow == $edow ? 0 : 1) - $bdays - $edow;
return max($bdays - 2, 0) + ($ydays / 7 * 5) + min($edow, 5);
}
say workdays(shift // 2021);
I know, I know… I’m always translating stuff, but it works!!!