# Count how many months are having a full 31 days by counting knuckles

How many of you that still use your own knuckle to determine whether a month is having a full 31 days or less?

You job is to write a program to count how many months, in a month range, are having a full 31 days and how many are having less than 31 days by "counting the knuckles".

Courtesy: amsi.org.au

## Input

A pair of months, the first of which doesn't have to come chronologically before the second, given in any suitable format. For instance: 201703 201902 — March 2017 to February 2019. Please describe the input format you choose. Note that the input must be able to include all years from 1 to 9999. The range of months specified includes both the starting and ending months.

## Output

Two integers: the number of months in the given range with 31 days and the number of months in the range with less than 31 days.

Example: 14 10 — 14 knuckles, 10 grooves (it means that in that month range we have 14 months that have a full 31 days, and 10 months that have less than 31 days).

For an input where the second month in the range comes chronologically before the first, for example 201612 201611, you have to output a pair of zero.

## Examples of input and output

| Input         | Output      |
|---------------|-------------|
| 201703 201902 | 14 10       |
| 201701 202008 | 26 18       |
| 000101 999912 | 69993 49995 |
| 201802 201803 | 1 1         |
| 201601 201601 | 1 0         |
| 201612 201611 | 0 0         |


## Rules

• You may choose any language you like
• One input per line
• This is , so shortest code in bytes wins!
• The winner will be chosen in April 9
• Standard loopholes apply
• PS: this is my first question in PCG, it might have some inconsistencies. Feel free to edit and confirm what's unclear for you.
• You say the first of which is guaranteed to come chronologically before the second, but this is false for test case 201612 201611. Commented Mar 30, 2017 at 7:23
• There exists a sandbox on the site where you can post the question solely to get all the doubts cleared. Commented Mar 30, 2017 at 8:05
• Thirty days hath September, April, June, and November. After February's done, all the rest have thirty-one. That's how I remember it. Commented Mar 30, 2017 at 12:33
• @AdmBorkBork The only trouble with that is the infinite looping (mandatory xkcd reference). Commented Mar 30, 2017 at 18:19
• I presume you intend to use the Gregorian calendar throughout the date range? Commented Mar 30, 2017 at 19:57

# Jelly, 21 bytes

ḅ12r/ị7RḂṁ12¤żC$S×⁼Ṣ$


Takes input like [[y, m], [y, m]].

Try it online!

### How it works

ḅ12r/ị7RḂṁ12¤żC$S×⁼Ṣ$  Main link. Argument: [[a, b], [c, d]]

ḅ12                    Unbase 12; yield [x, y] := [ 12a + b, 12c + d].
r/                  Reduce by range; yield [x, ..., y].
¤           Combine the five links to the left into a niladic chain.
7                  Set the return value to 7.
R                 Range; yield [1, 2, 3, 4, 5, 6, 7].
Ḃ                Bit; yield [1, 0, 1, 0, 1, 0, 1].
ṁ12             Mold 12; repeat the Booleans to create an array of length
12. Yields [1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1].
ị                 At-index; yield the elements of the array to the right at
the indices (1-based and modular) of the array to the left.
$Combine the two links to the left into a monadic chain. C Complement; map t -> 1-t over the array. ż Zip the original array with the complements. S Take the sum of each column.$  Combine the two links to the left into a monadic chain.
Ṣ      Sort [[a, b], [c, d]].
⁼     Compare the result with [[a, b], [c, d]], yielding 1 if
the input is sorted, 0 if not.
×     Multiply the results to both sides.


## JavaScript (ES6), 706867 64 bytes

Takes input as two integers in yyyymm format, in currying syntax (a)(b). Outputs an array of two integers [knuckles, grooves].

a=>g=(b,c=d=0)=>a>b?[c,d-c]:g(--b,c+!((b%=100)>11||b/.87&!!++d))


### Formatted and commented

a =>                        // main function: takes start date (a) as input / returns g
g = (                     // recursive function g, which takes:
b,                  //   - b = end date
c = d = 0           //   - c = number of knuckles
) =>                  // and also keeps track of: d = total number of months
a > b ?                 // if a is greater than b:
[ c, d - c ]          //   stop recursion and return the final result
:                       // else:
g(                    //   do a recursive call to g():
--b,                //   - decrement the end date
c +                 //   - increment the # of knuckles if
!(                  //     both of these conditions are false:
(b %= 100)        //     - the end month (now stored in b in 0-based indexing)
> 11 ||           //       is greater than 11
b / 0.87 & !!++d  //     - the number of days in this month is not 31
)                   //       (at the same time, d is incremented if the first
)                     //       condition is false)


### Test cases

NB: The third test case is not included in this snippet, because it won't work unless your browser has Tail Call Optimization enabled.

let f =

a=>g=(b,c=d=0)=>a>b?[c,d-c]:g(--b,c+!((b%=100)>11||b/.87&!!++d))

console.log(f(201703)(201902)); // 14 10
console.log(f(201701)(202008)); // 26 18
console.log(f(201802)(201803)); // 1 1
console.log(f(201601)(201601)); // 1 0
console.log(f(201612)(201611)); // 0 0        

# Python 2, 929086 80 bytes

lambda a,b,c,d:[(bin(2741)[2:]*(c+1-a))[b-1:-12+d or None].count(x)for x in'10']


Try it online!

6 more by converting to a lambda, with thanks to @math_junkie for the idea. Now outputs a list containing the two numbers.

Previous non-lambda version (86 bytes)

a,b,c,d=input()
for x in'10':print(bin(2741)[2:]*(c+1-a))[b-1:-12+d or None].count(x),


Try it online old!

2 saved with thanks to @ovs for helping me get rid of the len(k). I hadn't thought about using None.

Input is a list of integers in the format y1,m1,y2,m2

Some credit due to @KeerthanaPrabhakaran who got bin(2741)[2:] before I did which saves 1 byte over hard coding the binary string.

• Impressive... k=bin(2741)[2:]*(c+1-a) blew my mind Commented Mar 30, 2017 at 11:11
• This is 2 bytes shorter
– ovs
Commented Mar 30, 2017 at 14:13
• Brilliant! I was trying to work out hoe to get rid of the len(k) from the last slice. Thanks. Commented Mar 30, 2017 at 14:16
• You can save 6 bytes by using a lambda: TIO Commented Mar 30, 2017 at 15:17

# PHP, 259256249248237 221 bytes

Input Format: yyyymm,yyyymm

$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";foreach(new DatePeriod($_(Ym,$i[0]),new DateInterval(P1M),$_(Ym,$i[1])) as$f)date(t,mktime(0,0,0,$f->format(m),1,$f->format(y)))>30?++$x:++$y; echo$x.' '.++$y;  Try it online! ### Older versions $i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_("Ym",$i[0]),new DateInterval('P1M'),$_("Ym",$i[1])) as$f)date("t",mktime(0,0,0,$f->format("m"),1,$f->format("y")))==31?++$x:++$y; echo$x.' '.$y;  Try it online! $i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_(Ym,$i[0]),DateInterval::createFromDateString('1 month'),$_(Ym,$i[1])) as$f)date(t,mktime(0,0,0,$f->format(m),1,$f->format(y)))>30?++$x:++$y; echo$x.' '.$y;  Try it online! $i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_(Ym,$i[0]),DateInterval::createFromDateString('1 month'),$_(Ym,$i[1])) as$f)date(t,mktime(0,0,0,$f->format(m),1,$f->format(y)))==31?++$x:++$y; echo$x.' '.$y;  Try it online! $i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";foreach(new DatePeriod($_("Ym",$i[0]),DateInterval::createFromDateString('1 month'),$_("Ym",$i[1])) as$f)date("t",mktime(0,0,0,$f->format("m"),1,$f->format("y")))==31?++$x:++$y;
echo $x.' '.++$y;


Try it online!

$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_("Ym",$i[0]),DateInterval::createFromDateString('1 month'),$_("Ym",$i[1])) as$f)date("t",mktime(0,0,0,$f->format("m"),1,$f->format("y")))==31?++$x:++$y;
echo $x.' '.$y;


Try it online!

## Batch, 93 bytes

@set/ag=(y=%2/100-%1/100)*5+(x=%2%%100+6)*5/12-(w=%1%%100+5)*5/12,k=y*12+x-w-g
@echo %k% %g%


Accepts two parameters in ymm format (i.e. 101 - 999912). Previous 129-byte loop-based solution:

@set/al=%1-%1/100*88,u=%2-%2/100*88,k=g=0
@for /l %%i in (%l%,1,%u%)do @set/a"m=%%i%%12,k+=1451>>m&1,g+=2644>>m&1
@echo %k% %g%

• Incorrect result for 000101 999912, probably because of the integer size limit? Commented Mar 30, 2017 at 9:41
• @officialaimm Wrong input format, sorry - year should not have leading zeros.
– Neil
Commented Mar 30, 2017 at 10:18

# Python 3.5 (164162154152150148140 137 bytes)

n=int;a,b=input().split();t=k=0
for r in range(n(a[4:]),(n(b[:4])-n(a[:4]))*12+n(b[4:])+1):t+=1;k+=n('101010110101'[r%12-1])
print(k,t-k)


## repl.it

### prints output as number_of_knuckles number_of_grooves

• saved 2 bytes: Thanks to Cole
• saved 8 bytes: removed unwanted variables
• saved 2 bytes: reduced t=0;k=0 as t=k=0
• saved 2 bytes: Thanks to Cole (I had missed this before)
• saved 2 bytes: Thanks to Keerthana
• saved 8 bytes: removed unwanted variables
• saved 3 bytes: Thanks to math_junkie ( split(' ') to split() )
• I think you can reduce some bytes by doing n=int and also perhaps some exec tomfoolery.
– cole
Commented Mar 30, 2017 at 7:03
• I think you can do 2773&1<<r%12-1>0 instead of int('101010110101'[r%12-1]) Commented Mar 30, 2017 at 7:53
• @Loovjo I am getting error in doing so! Commented Mar 30, 2017 at 8:06
• using print([k,t-k]) as print(k,t-k) would produce the desired result as (k,g) thereby reducing 2 bytes! Commented Mar 30, 2017 at 9:12
• I believe you can replace split(' ') with split() Commented Mar 30, 2017 at 13:40

# Python 2, 147146 142 bytes

def s(a,b):y=100;r=bin(2741)[2:];x=b/y-a/y;i=r*(x-1);return((0,0),map((i+r[a%y-1:]+r[:b%y]if i or x else r[a%y-1:b%y]).count,('1','0')))[a<=b]


Try it online!

• Saved 4 bytes - Thanks to @math_junkie for suggesting the if-else clause with array lookup!

Breaking down the code,

def s(a,b):
y=100
r=bin(2741)[2:] #'101010110101'
x=b/y-a/y #to get the difference between the two years
i=r*(x-1)
return((0,0),map((i+r[a%y-1:]+r[:b%y]if i or x else r[a%y-1:b%y]).count,('1','0')))[a<=b]

• You can save bytes by replacing the if-else clauses with array lookups. See this post for details Commented Mar 30, 2017 at 15:07
• That was indeed a cool way! Didn't know that before! Thanks! Commented Mar 30, 2017 at 15:22

# PHP, 12010397 96 bytes

for($f=strtotime;$f($argv[2])>=$n=$f($argv[1].+$x++.month);)$k+=date(t,$n)>30;echo+$k,_,$x-1-$k;


Run like this:

php -nr 'for($f=strtotime;$f($argv[2])>=$n=$f($argv[1].+$x++.month);)$k+=date(t,$n)>30;echo+$k,_,$x-1-$k;' 0001-01 9999-12;echo
> 69993_49995


# Explanation

for(
$f=strtotime; # Alias strtotime function which is called twice.$f($argv[2]) >= # Create end date timestamp. Iterate until the end # date is reached.$n=$f($argv[1].+$x++.month # Create timestamp from start date + X months. ); )$k+=date(t,$n) > 30; # If "t" of current date (days in month) is 31 # increment$k (knuckles).

echo+$k,_,$x-1-$k; # Compute grooves (iterations -$k) and output,
# implicit cast to int to account for 0 count.


# Tweaks

• Saved 17 bytes by using timestamp style instead of DateTime object style
• Saved 6 bytes by not assigning end date timestamp to variable $e, just compare directly • Saved 1 byte by not keeping count of grooves, but just computing it after the loop • $x++ instead of +$x++ also works. Commented Mar 31, 2017 at 17:24 • @Titus, I had that at first, but realized that with $x uninitialized the string would be 2017-12month, which is an unrecognized format and results in 1970 Commented Apr 3, 2017 at 13:37
• Wicked ... it worked somewhere. Wicked enough though that it works without a literal + in the string. Commented Apr 4, 2017 at 12:33

# PowerShell, 96 bytes

for($a,$b=[datetime[]]$args;$a-le$b;$a=$a.AddMonths(1)){$x++;$z+=$a.Month-in2,4,6,9,11};$x-$z;$z  Try it online! Takes input as form 2017-03. Uses the built-in .NET date libraries and loops through from inputs $a to $b, each iteration incrementing $x++ and adding to $z if the current .Month is -in 2,4,6,9,11 (i.e., a non-31-day month). Then we output our total months minus the non-31-day months $x-$z, and the non-31-day months $z.

Tosses an error on the 0001-01 to 9999-12 test case, because .NET only supports years up to 9999, so the final .AddMonths(1) causes an overflow. Still outputs the correct values, though, because it's a non-terminating error; it just causes the loop to exit.

Probably would be shorter to do this arithmetically, like the Python or JavaScript answers, but I wanted to show an approach using the .NET built-ins.

# Bash, 113 bytes

s="$1-1";e="$2-1";sort <(while [ "$s" \< "$e" ];do s=$(date +%F -d"$s+1month");date +%d -d"$s-1day";done)|uniq -c  Try it online! needs golfing... takes input as 2016-03 2018-10 outputs:  1 28 7 30 12 31  ungolfed: s="$1-1"
e="$2-1" # adds first day of month to the dates sort <( while [ "$s" \< "$e" ]; do #iterates over dates s=$(date +%F -d"$s+1month") #adds one month to start date date +%d -d"$s-1day"         #outputs last day of previous month
done) | uniq -c              #counts ocurrences of day number prevously sorted


# Swift, 151 bytes

let f={(m:[Int])->[Int] in var k=[0,0]
(m.min()!...m.max()!).map{$0%100}.filter{$0>0&&\$0<13}.forEach{m in let n = m>7 ?m-7:m
k[(n%2+1)%2]+=1}
return k}


input is an array of two integers in the format as per the example