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

11

0

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".

Counting days of month by 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.

Rizki Pratama

Posted 2017-03-30T05:01:30.360

Reputation: 213

5You say the first of which is guaranteed to come chronologically before the second, but this is false for test case 201612 201611. – Dennis – 2017-03-30T07:23:11.273

2

There exists a sandbox on the site where you can post the question solely to get all the doubts cleared.

– ghosts_in_the_code – 2017-03-30T08:05:39.983

1Thirty days hath September, April, June, and November. After February's done, all the rest have thirty-one. That's how I remember it. – AdmBorkBork – 2017-03-30T12:33:50.500

@AdmBorkBork The only trouble with that is the infinite looping (mandatory xkcd reference).

– wizzwizz4 – 2017-03-30T18:19:57.163

I presume you intend to use the Gregorian calendar throughout the date range? – Paused until further notice. – 2017-03-30T19:57:58.047

@Dennis and all, Just came back online today. I'm going to clarify that the last test case is valid. Actually I wrote a rule for that, before it got edited. Check back my edited question again. Thanks!

– Rizki Pratama – 2017-03-31T02:16:53.633

@softtama That's unfortunate, as it invalidates at least four answers, including my own. While I realize now that the conflict was caused by someone else's edit, it's usually preferable to chose the option that doesn't invalidate answers, unless doing so actively hurts the challenge. – Dennis – 2017-03-31T15:34:56.147

Answers

7

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.

Dennis

Posted 2017-03-30T05:01:30.360

Reputation: 196 637

5

Python 2, 92 90 86 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.

ElPedro

Posted 2017-03-30T05:01:30.360

Reputation: 5 301

Impressive... k=bin(2741)[2:]*(c+1-a) blew my mind – officialaimm – 2017-03-30T11:11:55.930

1This is 2 bytes shorter – ovs – 2017-03-30T14:13:09.180

Brilliant! I was trying to work out hoe to get rid of the len(k) from the last slice. Thanks. – ElPedro – 2017-03-30T14:16:53.020

1

You can save 6 bytes by using a lambda: TIO

– math junkie – 2017-03-30T15:17:43.553

5

JavaScript (ES6), 70 68 67 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        

Arnauld

Posted 2017-03-30T05:01:30.360

Reputation: 111 334

4

PHP, 259 256 249 248 237 221 bytes

Outgolfed by aross: https://codegolf.stackexchange.com/a/114512/38505

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!

ʰᵈˑ

Posted 2017-03-30T05:01:30.360

Reputation: 1 426

3

Python 3.5 (164 162 154 152 150 148 140 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

takes input in the form of yyyymm yyyymm

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() )

officialaimm

Posted 2017-03-30T05:01:30.360

Reputation: 2 739

1I think you can reduce some bytes by doing n=int and also perhaps some exec tomfoolery. – cole – 2017-03-30T07:03:33.353

1I think you can do 2773&1<<r%12-1>0 instead of int('101010110101'[r%12-1]) – Loovjo – 2017-03-30T07:53:51.993

@Loovjo I am getting error in doing so! – officialaimm – 2017-03-30T08:06:00.947

1using print([k,t-k]) as print(k,t-k) would produce the desired result as (k,g) thereby reducing 2 bytes! – Keerthana Prabhakaran – 2017-03-30T09:12:40.133

1I believe you can replace split(' ') with split() – math junkie – 2017-03-30T13:40:02.857

3

Python 2, 147 146 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]

Keerthana Prabhakaran

Posted 2017-03-30T05:01:30.360

Reputation: 759

1

You can save bytes by replacing the if-else clauses with array lookups. See this post for details

– math junkie – 2017-03-30T15:07:52.277

That was indeed a cool way! Didn't know that before! Thanks! – Keerthana Prabhakaran – 2017-03-30T15:22:28.953

3

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%

Neil

Posted 2017-03-30T05:01:30.360

Reputation: 95 035

Incorrect result for 000101 999912, probably because of the integer size limit? – officialaimm – 2017-03-30T09:41:30.840

1@officialaimm Wrong input format, sorry - year should not have leading zeros. – Neil – 2017-03-30T10:18:54.357

3

PHP, 120 103 97 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

aross

Posted 2017-03-30T05:01:30.360

Reputation: 1 583

$x++ instead of +$x++ also works. – Titus – 2017-03-31T17:24:38.757

@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 – aross – 2017-04-03T13:37:52.687

Wicked ... it worked somewhere. Wicked enough though that it works without a literal + in the string. – Titus – 2017-04-04T12:33:29.580

2

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.

AdmBorkBork

Posted 2017-03-30T05:01:30.360

Reputation: 41 581

2

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

marcosm

Posted 2017-03-30T05:01:30.360

Reputation: 986

1

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

John McDowall

Posted 2017-03-30T05:01:30.360

Reputation: 161