GNU date+bc, 56 bytes
Abstract
This Answer demonstrates that the following 56-byte shell pipeline
calculates the percentage of work days in a given month, rounded to
the nearest whole percent:
date -d$1-1month-4week +'70+%d-3*(%w+1<%d)-3*(%u<%d)'|bc
Analysis
There are 28 possible monthly calendars, that can be identified
according to the last day/date of the month:
|
Mon |
Tue |
Wed |
Thu |
Fri |
Sat |
Sun |
28th |
20/28 |
20/28 |
20/28 |
20/28 |
20/28 |
20/28 |
20/28 |
29th |
21/29 |
21/29 |
21/29 |
21/29 |
21/29 |
20/29 |
20/29 |
30th |
21/30 |
22/30 |
22/30 |
22/30 |
22/30 |
21/30 |
20/30 |
31st |
21/31 |
22/31 |
23/31 |
23/31 |
23/31 |
22/31 |
21/31 |
There are nine distinct fractions here, that can be converted to
percentages:
|
20/ |
21/ |
22/ |
23/ |
/28 |
71 |
|
|
|
/29 |
69 |
72 |
|
|
/30 |
67 |
70 |
73 |
|
/31 |
|
68 |
71 |
74 |
Substituting these gives our conversion table:
|
Mon |
Tue |
Wed |
Thu |
Fri |
Sat |
Sun |
28th |
71 |
71 |
71 |
71 |
71 |
71 |
71 |
29th |
72 |
72 |
72 |
72 |
72 |
69 |
69 |
30th |
70 |
73 |
73 |
73 |
73 |
70 |
67 |
31st |
68 |
71 |
74 |
74 |
74 |
71 |
68 |
The pattern becomes more clearly evident when we split this into a
basic value for a month containing four weekends, less an amount for a
fifth Saturday and for a fifth Sunday:
|
Basic |
Thu |
Fri |
Sat |
Sun |
Mon |
Tue |
Wed |
28th |
71 |
. . |
. . |
. . |
. . |
. . |
. . |
. . |
29th |
72 |
. . |
. . |
. -3 |
-3 . |
. . |
. . |
. . |
30th |
73 |
. . |
. . |
. -3 |
-3 -3 |
-3 . |
. . |
. . |
31st |
74 |
. . |
. . |
. -3 |
-3 -3 |
-3 -3 |
-3 . |
. . |
The basic value is clearly 71 plus one for each day more than 28 days.
And we must then subtract 3 if the last day is in a range beginning
Saturday and having length equal to those excess days, less three for
a similar range beginning Sunday.
Implementation
Firstly, we append a day number to turn year and month into a full
date: $1-01
.
Rather than finding the last day of the month, we select a date
nearer the start of the month, by subtracting 28 days from the start
of the following month. This gives us a day-of-week that's one later
than used in the analysis section:
date -d'$1-01 +1month-4week'
Using this date, we then format a calculation for bc
to execute and
print. The basic percentage is simple; it's just 70 more than the day
number we landed on: 70+%d
.
The adjustments for extra weekend days are then computed using a pair
of inequalities, taking advantage of the fact that there are two
representations for day-of-week, with Sunday represented as 0 or
as 7. We subtract 3 points from the basic percentage for each of
those inequalities.
Demo
We can reproduce the results from the examples in the question:
And we can show that we get the correct results for all 28 shapes of
month
shown in the first table:
for i in $(printf '%s\n' 20{{00..03}-{03..12},{00..24}-02}-01+month-1day |
date -f- +'%Y-%m%t%d %u %a' | sort -k2,3 -u | cut -f1)
do ./1158 $i
done | rs 0 7
71 71 71 71 71 71 71
72 72 72 72 72 69 69
70 73 73 73 73 70 67
68 71 74 74 74 71 68
echo 100
\$\endgroup\$