Percentage of work days in a month

Given a year and a month, find out the percentage of work days in said month. Work days are Monday through Friday with no regard to holidays or other special things. The Gregorian calendar is used.

Input

A year and month in ISO 8601 format (YYYY-MM). The year always has four digits, the month always has two digits. The given year will not be before 1582.

Output

Output is the percentage of work days (according to above definition) in the given month, rounded to a whole number. No percent sign or fractional digits follow.

Sample 1

Input                Output

2010-05              68

Sample 2

Input                Output

2010-06              73

Sample 3

Input                Output

1920-10              68

Sample 4

Input                Output

2817-12              68

A week has passed, an answer has been accepted. For the curious, the sizes of the submissions we got in our contest:

129 – Z shell
174 – VB.NET
222 – C
233 – C
300 – C

As well as our own (unranked) solutions:

75 – PowerShell
93 – Ruby
112 – Bourne shell

• I'm a graduate student, so... echo 100 – Amory Nov 24 '14 at 20:24
• Even grad students cannot escape the fundamental definitions in their line of work. And I defined work days differently ;-) – Joey Nov 24 '14 at 21:42

64-bit Perl, 67 68

Perl 5.10 or later, run with perl -E 'code here' or perl -M5.010 filename

map{$d++,/^S/||$w++if$_=date -d@ARGV-$_}1..31;say int.5+100*$w/$d

Concessions to code size:

• locale-sensitive: it counts as work days the days whose date output don't start with a capital S. Run under LC_ALL=C if in doubt.
• output is pure and well-formatted, but there's "garbage" on stderr on months shorter than 31. 2> /dev/null if upset.
• for some reason, my version of date considers 2817-12 an invalid month. Who knew, GNU new apocalypse is due! Requires a 64 bit build of date for dates after 2038. (Thanks Joey)
• Apparently it was abolished by "Siphous Hemes" during his rule. ref "A new history of the Holy Bible" – Martin York Feb 24 '11 at 8:51
• Is every year after 2038 broken? Then switching t a 64-bit build might help due to some braindead-ness with date handling ;-) – Joey Feb 26 '11 at 10:51
• @Joey that's exactly it. Thanks for the tip! – J B Feb 28 '11 at 13:50
• JB: Was just a guess and I actually didn't expect anything beyond C to still use solely 32-bit integers that count seconds since a weird epoch. Though, to be honest, I put the requirement about dates > 2038 in there for exactly this purpose ;-) – Joey Feb 28 '11 at 18:24

PHP - 135

I made it in PHP because I had a similar problem to treat a few days ago.

<?php $a=array(2,3,3,3,2,1,1);$t=strtotime($argv[1]);$m=date(t,$t);echo round((20+min($m-28,$a[date(w,strtotime('28day',$t))]))/$m*100) (Somewhat) More legibly, and without notices about constants being used as strings: <?php date_default_timezone_set('America/New_York');$additionalDays = array(2, 3, 3, 3, 2, 1, 1);
$timestamp = strtotime($argv[1]);
$daysInMonth = date('t',$timestamp);
$limit =$daysInMonth - 28;
$twentyNinthDayIndex = date('w', strtotime("+28 days",$timestamp));
$add =$additionalDays[$twentyNinthDayIndex];$numberOfWorkDays = 20 + min($limit,$add);
echo round($numberOfWorkDays /$daysInMonth * 100);
?>

This is made possible by a very simple algorithm to compute the number of work days in a month: check for the weekdayness of the 29th, 30th and 31st (if those dates exist), and add 20.

• Great algorithm, poor golfing. Using contemporary PHP 5.3.5 and -R, this approach can be golfed down to 86 bytes (63.7%): $a="2333211";echo.5+min(-8+$m=date(t,$t=strtotime($argn)),20+$a[date(w,$t)])/$m*100|0; See the golfing steps. – Titus Mar 1 '17 at 23:53 • 80 bytes: <?=.5+min(-8+$m=date(t,$t=strtotime($argn)),20+(5886>>date(w,$t)*2&3))/$m*100|0; – Titus Nov 26 '18 at 12:52

Python 152 Characters

from calendar import*
y,m=map(int,raw_input().split('-'))
c=r=monthrange(y,m)[1]
for d in range(1,r+1):
if weekday(y,m,d)>4:c-=1
print '%.f'%(c*100./r)

Bash + coreutils, 82 bytes

f()(cal -NMd$1|sed -n "s/^$2.//p"|wc -w)
dc -ef $1 "[^S ]"df$1 S+r200*r/1+2/p

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++$b+=!!($_%6)} [int]($b*100/$a) • Are you sure [int] really rounds? I'd tend to believe it floors. – zneak Feb 28 '11 at 4:08 • @zneak: PowerShell is not C or a C-derived language. It uses the default rounding mode of .NET which is »round to nearest even integer«. Just try it out: Both [int]1.5 and [int]2.5 yield 2. This exact behaviour often causes problems in tasks where floored division is necessary (which then requires an extra [Math]::Floor()), but in this case it doesn't hurt and »round to even« only applies to numbers that end in .5 which cannot happen here. – Joey Feb 28 '11 at 18:23 • If you're sure then I believe you. I just expected it to work like C# instead, and I don't have any Windows machine on which to test at home. – zneak Feb 28 '11 at 20:09 • @zneak: No, definitely doesn't work like in C#. Something like [int] in PowerShell is usually more a conversion than a cast :-). Things like [int[]][char[]]'abc' also work which you can't get to work in many other languages. – Joey Feb 28 '11 at 20:59 • Necrobump but$input -> $args saves a byte. – Veskah Dec 5 '18 at 0:19 D: 186 Characters auto f(S)(S s){auto d=Date.fromISOExtendedString(s~"-28"),e=d.endOfMonth;int n=20;while(1){d+=dur!"days"(1);if(d>e)break;int w=d.dayOfWeek;if(w>0&&w<6)++n;}return rndtol(n*100.0/e.day);} More Legibly: auto f(S)(S s) { auto d = Date.fromISOExtendedString(s ~ "-28"), e = d.endOfMonth; int n = 20; while(1) { d += dur!"days"(1); if(d > e) break; int w = d.dayOfWeek; if(w > 0 && w < 6) ++n; } return rndtol(n * 100.0 / e.day); } Python - 142 from calendar import* y,m=map(int,raw_input().split('-')) f,r=monthrange(y,m) print'%.f'%((r-sum(weekday(y,m,d+1)>4for d in range(r)))*100./r) Thanks to fR0DDY for the calendar bit. Ruby, 124119 111 require 'date' e=Date.civil *$*[0].split(?-).map(&:to_i),-1
p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round

Requires Ruby 1.9 due to splatting the year and month before the -1 "day" argument and ?- for "-". For Ruby 1.8, we must add 2 characters:

require 'date'
e=Date.civil *$*[0].split('-').map(&:to_i)<<-1 p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round Edit: Shave five characters based on @Dogbert's help. Edit: Shave eight more characters based on @steenslag's help. • Why are you assigning Date to D? – Dogbert Feb 28 '11 at 11:48 • @Dogbert Whoops! Holdover from a time when I had two Date.civils; thanks! – Phrogz Feb 28 '11 at 15:43 • '-' could be written as ?- in Ruby 1.9 – Dogbert Feb 28 '11 at 16:03 • @Dogbert Nice. I'll throw that in, too. I feel there must be a shorter way to pick the week days, but I haven't found it yet. – Phrogz Feb 28 '11 at 16:22 • e+1<<1 is three shorter than e-e.day+1 – steenslag Mar 2 '11 at 17:03 PHP 5.2, 88 bytes Although I already golfed zneak´s solution down to 85 bytes (I just found one more), here´s my own: I doubt that I can squeeze another three bytes out here.$a=_4444444255555236666304777411;echo$a[date(t,$t=strtotime($argn))%28*7+date(N,$t)]+67;

takes input from STDIN: Run with echo <yyyy>-<mm> | php -nR '<code>'.

The string $a maps days per month (date(t)) and week day of the first day of the month (date(N): Monday=1, Sunday=7) to the percentage of work days-67; strtotime converts the input to a UNIX timestamp; the rest of the code does the hashing. +1 byte for older PHP 5: Replace N with w and$a=_...; with $a="...". another +3 bytes for PHP 4: insert .-1 after$argn.

-5 bytes for PHP 5.5 or later (postdates the challenge):

Try it online!