Perl Weekly Challenge 138: Workdays

by Abigail

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

Discussion

Every year has 260, 261 or 262 workdays. The only variables are the on which day of the week the year starts, and whether the year is a leap year or not.

For regular years, if the year starts on weekday, it ends on the same weekday, and the year has 261 work days. If a regular year starts on a weekend, it ends on a weekend, and the year has 260 work days.

For leap years, if the year starts on a Monday to Thursday, it ends on Tuesday to Friday, giving the year 262 work days. If a leap year starts on a Friday, it ends on a Saturday, giving 261 work days. If a leap year starts on a Saturday, it ends on a Sunday, giving 260 work days. If a leap year starts on a Sunday, it ends on a Monday, giving the year 261 work days. So, we can just a lookup table, with 14 values (7 for regular years, and 7 for leap years).

But we don't have to calculate the weekday of Jan 1. As long as we have the weekday of a particular date, there will be fixed 'offset' from that date to Jan 1 (the offset may differ between a leap year and a regular year). For this challenge, we will use the doomsday value, which we have used as in the alternative solution of the challenge of week 137.

This gives the following lookup table:

Regular Year Leap Year
Doomsday Value Jan 1 Work days Jan 1 Work days
0 Friday 261 Thursday 262
1 Saturday 260 Friday 261
2 Sunday 260 Saturday 260
3 Monday 261 Sunday 261
4 Tuesday 261 Monday 262
5 Wednesday 261 Tuesday 262
6 Thursday 261 Wednesday 262

Live Demo

Enter a year in the (proleptic) Gregorian calendar, and hit the Calculate button to calculate the number of work days in that year.

Enter a year:

Solution

Our solutions will be reading years from standard input (one year per line), and write the number of work days of those year to standard output.

Perl

First, we copy the calculation of the doomsday value and the check whether a year is a leap year from the previous week:

my $SUNDAY    = 0;
my $MONDAY    = 1;
my $TUESDAY   = 2;
my $WEDNESDAY = 3;
my $THURSDAY  = 4;
my $FRIDAY    = 5;
my $SATURDAY  = 6;

sub doomsday ($year = $_) {
    use integer;
    my $anchor   = ($TUESDAY, $SUNDAY, $FRIDAY, $WEDNESDAY) [($year / 100) % 4];
    my $y        = $year % 100;
    my $doomsday = ((($y / 12) + ($y % 12) + (($y % 12) / 4)) + $anchor) % 7;
    $doomsday;
}

sub is_leap ($year = $_) {
    ($year % 400 == 0) || ($year % 4 == 0) && ($year % 100 != 0) ? 1 : 0
}

Then it's just a matter of defining a lookup table, reading the input, and printing the value we can look up:

while (<>) {
    say $lookup [is_leap] [doomsday]
}

Find the full program on GitHub.

Other languages

We also have implementation in a bunch of different language; they all use the same algorithm as the Perl implementation:

AWK, Bash, bc, C, Go, Java, Lua, Node.js, Pascal, Python, R, Ruby, Scheme, and Tcl.


Please leave any comments as a GitHub issue.