25
\$\begingroup\$

Given a textual representation (case-insensitive full name or 3 character abbreviation) of a month return the number of days in the month.

For example, december, DEC, and dec should all return 31.

February can have either 28 or 29 days.

Assume the input is a month in one of the correct forms.

\$\endgroup\$
  • 19
    \$\begingroup\$ You should probably list out all the variations of the month names that we should be able to accept. \$\endgroup\$ – Giuseppe Oct 28 '17 at 17:40
  • 1
    \$\begingroup\$ For anybody who can use it, the ASCII ordinal sums of the first 3 characters lowered are unique. \$\endgroup\$ – totallyhuman Oct 28 '17 at 18:20
  • 19
    \$\begingroup\$ That was far, far too soon to accept a solution. \$\endgroup\$ – Shaggy Oct 28 '17 at 19:28
  • 5
    \$\begingroup\$ i think this would be nicer if input was just the month in a fixed format, as the format now basically requires converting to a fixed case and only looking at the first 3 letters. \$\endgroup\$ – xnor Oct 28 '17 at 21:04
  • 4
    \$\begingroup\$ As it stands it looks like you want answers to handle all of the listed forms - "For example, december, DEC, and dec should all return 31" - Is that the intention? \$\endgroup\$ – Jonathan Allan Oct 28 '17 at 23:27

27 Answers 27

4
\$\begingroup\$

Pyke, 9 bytes

l4C9@~%R@

Try it here!

l4        -   input.title()
    @     -  v.index(^)
  C9      -   ['PADDING', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
        @ - v[^]
     ~%R  -  ['Padding', 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

Or 15 bytes if all input formats are required

l43<C9 3L<@~%R@

Try it here!

l43<            -   input.title()[:3]
          @     -  v.index(^)
    C9 3L<      -   ['PAD', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
              @ - v[^]
           ~%R  -  ['Padding', 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
\$\endgroup\$
  • 6
    \$\begingroup\$ This returns 31 for FEB. \$\endgroup\$ – Laikoni Oct 29 '17 at 7:26
  • 2
    \$\begingroup\$ I believe @Laikoni's point is valid (it also returns 31 for Apr, Jun, Sep, and Nov) but also think that it requires a little clarification in the OP (see my question). \$\endgroup\$ – Jonathan Allan Oct 29 '17 at 11:20
  • \$\begingroup\$ @JonathanAllan Well, the OP has accepted this answer, so I guess it's valid? \$\endgroup\$ – Erik the Outgolfer Oct 29 '17 at 17:12
  • 4
    \$\begingroup\$ @EriktheOutgolfer I would not jump to that conclusion personally. \$\endgroup\$ – Jonathan Allan Oct 29 '17 at 17:41
  • \$\begingroup\$ I was under the impression that it only needed to work for one form of inputs \$\endgroup\$ – Blue Oct 29 '17 at 22:28
33
\$\begingroup\$

JavaScript (ES6),  48 47 44 43  42 bytes

m=>31^'311'[parseInt(m[1]+m[2],34)*3%49%8]

Demo

let f =

m=>31^'311'[parseInt(m[1]+m[2],34)*3%49%8]

;(
  'JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,' +
  'JANUARY,FEBRUARY,MARCH,APRIL,MAY,JUNE,JULY,AUGUST,SEPTEMBER,OCTOBER,NOVEMBER,DECEMBER'
).split`,`
.forEach(m => console.log(m, '->', f(m)))

How?

These operations lead to a lookup table of 8 entries, which would not be very interesting if the values were randomly distributed. But any result greater than 2 is mapped to 31 days. Therefore, only the first 3 entries need to be stored explicitly.

Month | [1:2] | Base 34 -> dec. | * 3  | % 49 | % 8 | Days
------+-------+-----------------+------+------+-----+-----
  JAN |    AN |             363 | 1089 |   11 |   3 |  31
  FEB |    EB |             487 | 1461 |   40 |   0 |  28
  MAR |    AR |             367 | 1101 |   23 |   7 |  31
  APR |    PR |             877 | 2631 |   34 |   2 |  30
  MAY |    AY |              10 |   30 |   30 |   6 |  31
  JUN |    UN |            1043 | 3129 |   42 |   2 |  30
  JUL |    UL |            1041 | 3123 |   36 |   4 |  31
  AUG |    UG |            1036 | 3108 |   21 |   5 |  31
  SEP |    EP |             501 | 1503 |   33 |   1 |  30
  OCT |    CT |             437 | 1311 |   37 |   5 |  31
  NOV |    OV |             847 | 2541 |   42 |   2 |  30
  DEC |    EC |             488 | 1464 |   43 |   3 |  31
\$\endgroup\$
  • 14
    \$\begingroup\$ honestly how on earth do you keep making these amazing weird submissions with crazy maths stuff D: do you have a program to find these or are you just too good for the rest of us \$\endgroup\$ – HyperNeutrino Oct 28 '17 at 17:56
  • 1
    \$\begingroup\$ @HyperNeutrino The first thing I try is always to find a base conversion, followed by an optional multiplication followed by one or several modulo operations. This one was found quickly that way. But I misread the challenge and first thought that this .substr(0,3) was not required. So, on second thought, this may not be the best approach. \$\endgroup\$ – Arnauld Oct 28 '17 at 18:04
  • \$\begingroup\$ substr? slice! \$\endgroup\$ – Neil Oct 28 '17 at 18:04
  • \$\begingroup\$ My trivial approach is only <s>2</s> 3 bytes longer so it might not be optimal anymore because of that, but still very impressive :) \$\endgroup\$ – HyperNeutrino Oct 28 '17 at 18:05
  • 1
    \$\begingroup\$ Someone's edit removed that part, but one of the reasons I originally disallowed it is I was wanting to see answers like this one. I love the use base 34 to sidestep the issue of capitalization and different formats. \$\endgroup\$ – qw3n Oct 29 '17 at 18:45
15
\$\begingroup\$

Javascript (ES6), 36 33 bytes

-3 bytes thanks to @JustinMariner and @Neil

m=>31-new Date(m+31).getDate()%31

Sorry @Arnauld, abusing JavaScript weirdness is shorter than your fancy base conversions.

How it works

For some reason, JavaScript allows entering dates outside of the specified month. The code counts how many days outside the month the date is to determine how many days there are in the month. Examples:
"FEB31"Thu Mar 02 200031 - 2 % 3129
"October31"Tue Oct 31 200031 - 31 % 3131

Test cases

f=m=>31-new Date(m+31).getDate()%31
;(
 "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,"+
 "January,February,Mars,April,May,June,July,August,September,October,November,December"
).split`,`.forEach(s=>console.log(s,"->",f(s)))

\$\endgroup\$
  • \$\begingroup\$ MS Excel also does this.. January 0 is always December Last Day, so =DAY("00/01/2017") will result in 31 \$\endgroup\$ – DavChana Oct 29 '17 at 17:18
  • \$\begingroup\$ It looks like Javascript only allows date strings where the day is up to 31. If you try to enter "feb 32", it translates to 2032-02-01, and if you try to force it with "0-feb-32" (or a similar string), it just says "Invalid Date". Oddly enough, if you set the day to 0 ("feb 0"), it translates to 2000-02-01 rather than 2000-01-31. \$\endgroup\$ – TehPers Oct 29 '17 at 20:14
  • \$\begingroup\$ You might be able to save a byte by dropping the space before 31. It seems to work in Chrome as new Date("feb31") for example. \$\endgroup\$ – Justin Mariner Oct 30 '17 at 7:56
  • \$\begingroup\$ In fact you could probably use +31 saving three bytes overall. None of this works in Firefox though. \$\endgroup\$ – Neil Oct 30 '17 at 11:15
11
\$\begingroup\$

Python 2, 46 45 38 bytes

-1 byte thanks to @totallyhuman

lambda m:29-int(m[1:3],35)%238%36%-5/2

Try it online!

\$\endgroup\$
7
\$\begingroup\$

Bash, 21 bytes

cal $1|xargs|tail -c3

Try it online!

Takes input as command-line argument and outputs with a trailing newline. The day count for February depends on that of the current year

Requires the util-linux 2.29 version of cal, which is the one available on TIO. Also is locale-dependent, so LC_TIME must be changed on non-English systems (thanks @Dennis for clarification).

Idea of piping through xargs to trim cal's output is from this SO answer.

\$\endgroup\$
  • 2
    \$\begingroup\$ This isn’t just bash. Generally it’s sh, but it’s probably almost every shell implementation that supports path lookups and pipes, on a system with cal, tail and xargs. \$\endgroup\$ – kojiro Oct 30 '17 at 1:26
5
\$\begingroup\$

Proton, 50 bytes

k=>31-((e=k.lower()[1to3])in"eprunov")-3*(e=="eb")

Try it online!

-14 bytes thanks to Jonathan Frech

Thirty days hath September, April, June, and November. All the rest had peanut butter. All except my grandmother; she had a little red trike, but I stole it. muahahahahaha

(I've been waiting to tell that joke (source: my math professor) for ages on this site :D :D :D)

\$\endgroup\$
  • \$\begingroup\$ @Riker oh whoops that wasn't there when I started writing this :/ \$\endgroup\$ – HyperNeutrino Oct 28 '17 at 17:52
  • 1
    \$\begingroup\$ There is a new rule that you have to check for not a valid month and return 0. I hope it gets deleted \$\endgroup\$ – Level River St Oct 28 '17 at 17:52
  • 1
    \$\begingroup\$ Nevermind changing I'm deleting that part \$\endgroup\$ – qw3n Oct 28 '17 at 17:53
  • \$\begingroup\$ I think you can use a single string 'sepaprjunnov' instead of a list of strings. \$\endgroup\$ – Jonathan Frech Oct 28 '17 at 17:55
  • \$\begingroup\$ @JonathanFrech maybe; I'll try that, thanks \$\endgroup\$ – HyperNeutrino Oct 28 '17 at 17:56
4
\$\begingroup\$

C# (.NET Core), 52+13=65 38+24=62 bytes

m=>D.DaysInMonth(1,D.Parse(1+m).Month)

Try it online!

+24 for using D=System.DateTime;

Acknowledgements

-3 bytes thanks to Grzegorz Puławski.

\$\endgroup\$
  • \$\begingroup\$ Does this work without using System;? Or can you excluse that from the byte count? \$\endgroup\$ – Matty Oct 30 '17 at 13:13
  • \$\begingroup\$ @Matty That's a good point; now added. \$\endgroup\$ – Ayb4btu Oct 30 '17 at 19:07
  • \$\begingroup\$ Late tip, but -3 bytes: using D=System.DateTime; and m=>D.DaysInMonth(1,D.Parse(1+m).Month) like here: tio.run/##jc5BSwMxEAXgs/… \$\endgroup\$ – Grzegorz Puławski Nov 10 '17 at 9:58
3
\$\begingroup\$

Python 3, 60 bytes

x=input().lower()[1:3];print(31-(x in"eprunov")-3*(x=="eb"))

Try it online!

Porting my Proton solution

-10 bytes thanks to totallyhuman

\$\endgroup\$
  • \$\begingroup\$ Better than mine heh \$\endgroup\$ – Thomas Ward Oct 28 '17 at 18:11
  • 1
    \$\begingroup\$ um \$\endgroup\$ – totallyhuman Oct 28 '17 at 18:12
  • \$\begingroup\$ :P builtins are sometimes too long :P \$\endgroup\$ – HyperNeutrino Oct 28 '17 at 18:12
  • \$\begingroup\$ @totallyhuman oh rly wow. +1 thanks :P \$\endgroup\$ – HyperNeutrino Oct 28 '17 at 18:12
  • 2
    \$\begingroup\$ umm \$\endgroup\$ – totallyhuman Oct 28 '17 at 18:23
3
\$\begingroup\$

Shell/GNU Date, 39, 26 bytes

date -d1$1+1month-1day +%d

Where $1 is the name of the month.

Try it online!

edit: Thanks Dennis for saving many bytes!

\$\endgroup\$
2
\$\begingroup\$

AWK, 45 44 bytes

L=tolower($1){$0=L~/v|p|un/?30:L~/f/?28:31}1

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Python 3 - 93 86 84 82 bytes

Variants of answer (showing the progression of time, and bytes for each, with TIO links):

Original Answer (93 bytes)

-7 bytes thanks to Jonathan Frech. (86 bytes)

-2 more bytes thanks to my own further testing of the monthrange results, with the second value always being the higher value. (84 bytes) 1

-2 more by using import calendar as c and referencing it with c.monthrange. (82 bytes, current revision)


lambda x:c.monthrange(1,time.strptime(x[:3],'%b')[1])[1];import time,calendar as c

Obviously not as nice as HyperNeutrino's answer which doesn't use built-ins, but this still works.


Footnotes

1: Test cases via TIO.run showing the proof for how I'm handling those monthrange values, for a varying number of month test cases.

\$\endgroup\$
  • \$\begingroup\$ 86 bytes. \$\endgroup\$ – Jonathan Frech Oct 28 '17 at 18:27
  • \$\begingroup\$ @JonathanFrech Thanks. Further revised downwards by my having tested more of how monthrange works, and also by using import ...,calendar as c so as not having to type 'calendar' twice. \$\endgroup\$ – Thomas Ward Oct 28 '17 at 19:38
2
\$\begingroup\$

Perl 5, 47 + 1 (-p) = 48 bytes

$_=substr$_,1,2;$_=31-("eprunov"=~/$_/i)-3*/b/i

Try it online!

\$\endgroup\$
  • \$\begingroup\$ -6 bytes : ($_)=/.(..)/; instead of $_=substr$_,1,2; and () around "eprunov"=~/$_/i can be removed. \$\endgroup\$ – Nahuel Fouilleul Nov 3 '17 at 8:33
2
\$\begingroup\$

Haskell, 65 63 62 bytes

f.map((`mod`32).fromEnum)
f(_:b:c:_)|c<3=28|c>13,b>3=30
f _=31

Try it online!

Pattern matching approach. The first line is to handle the case-insensitivity. Then we return 28 if the third letter is smaller than C (number 3), 30 if the second letter is larger than C and the third one larger than M, or 31 otherwise.

Edit: -1 byte thanks to Leo


Alternative (65 64 bytes)

f s|let i#n=n<mod(fromEnum$s!!i)32=sum$29:[2|2#2]++[-1|2#13,1#3]

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ Clever one! You can save a byte by checking for c<3 instead of a==6 (February is the first month if you order them by their third letter, followed by December) \$\endgroup\$ – Leo Oct 30 '17 at 5:29
2
\$\begingroup\$

APL (Dyalog), 32 bytes*

Tacit prefix function. Assumes ⎕IO (Index Origin) 0, which is default on many systems.

31 28 30⊃⍨∘⊃'.p|un|no|f'⎕S 1⍠1

Try it online!

⍠1 case insensitively

1 return the length of the

⎕S PCRE Search for

'.p|un|no|f' any-char,"p" or "un" or "no" or "f"

⊃⍨∘⊃ and use the first element of that (0 if none) to pick from

31 28 30 this list

Thus:

  • Apr, Sep, Jun, and Nov will select the number at index 2, namely 30

  • Feb will select the number at index 1, namely 28

  • anything else will select the number at index 0, namely 31


* Using Classic and counting as ⎕OPT.

\$\endgroup\$
2
\$\begingroup\$

Mediawiki Template, 19 bytes

{{#time:t|{{{1}}}}}
\$\endgroup\$
1
\$\begingroup\$

MATL, 22 bytes

14L22Y2c3:Z)Z{kj3:)km)

Try it online!

Explanation

14L    % Push numeric array of month lengths: [31 28 ... 31]
22Y2   % Push cell array of strings with month names: {'January', ..., 'December'}
c      % Convert to 2D char array, right-padding with spaces
3:Z)   % Keep first 3 columns
Z{     % Split into cell array of strings, one each row
k      % Convert to lower case
j      % Input string
3:)    % Keep first 3 characcters
k      % Convert to lower case
m      % Ismember: gives a logical index with one match
)      % Use that as index into array of month lengths. Implicit display
\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 46 30 bytes

#~NextDate~"Month"~DayCount~#&

Try it online!

Will give either 28 or 29 for February depending on whether the current year is a leap year.

How it works

All date commands in Mathematica will interpret input such April, APR, ApRiL, and so on as the first day of the corresponding month in the current year. (As a bonus, input such as "February 2016" or {2016,2} also works as expected.)

#~NextDate~"Month" gives the first day of the month after that, and DayCount gives the number of days between its two arguments. The number of days between April 1st and May 1st is 30, the number of days in April.

\$\endgroup\$
1
\$\begingroup\$

Java 8, 47 bytes

m->31-new java.util.Date(m+"31 1").getDate()%31

Try it online!

Ended up using the same idea as Herman Lauenstein's JS answer, where setting the date to the 31st pushed into the next month. Java does require a year, so that has been set to 1.

\$\endgroup\$
1
\$\begingroup\$

Retina, 32 31 28 bytes

i`f
28
i`p|v|un
30
\D

^$
31

Try it online! Edit: Saved 1 byte thanks to @RobertBenson. Saved 3 bytes thanks to @ovs.

\$\endgroup\$
  • \$\begingroup\$ I believe you could save a byte by using 'f' instead of 'eb' \$\endgroup\$ – Robert Benson Oct 28 '17 at 18:19
  • \$\begingroup\$ 28 bytes \$\endgroup\$ – ovs Oct 30 '17 at 17:41
1
\$\begingroup\$

q/kdb+, 36 bytes

Solution:

28 30 31@2^1&(*)"ebeprunov"ss(_)1_3#

Examples:

q)28 30 31@2^1&(*)"ebeprunov"ss(_)1_3#"January"
31
q)28 30 31@2^1&(*)"ebeprunov"ss(_)1_3#"FEB"
28
q)28 30 31@2^1&(*)"ebeprunov"ss(_)1_3#"jun"
30

Explanation:

There are a million ways to skin a cat. I think is slightly different to the others. Take the 2nd and 3rd letters of the input, lowercase them, then look them up in the string "ebeprunov". If they are at location 0, then this is February, if they are at a location >0 they are a 30-dayer, if they are not in the string, they are a 31-dayer.

28 30 31@2^1&first"ebeprunov"ss lower 1_3# / ungolfed solution
                                        3# / take first 3 items from list, January => Jan
                                      1_   / drop the first item from the list, Jan => an
                                lower      / lower-case, an => an
                  "ebeprunov"ss            / string-search in "ebeprunov", an => ,0N (enlisted null)
             first                         / take the first, ,0N => 0N
           1&                              / take max (&) with 1, 0N => 0N
         2^                                / fill nulls with 2, 0N => 2
        @                                  / index into
28 30 31                                   / list 28,30,31
\$\endgroup\$
1
\$\begingroup\$

Excel VBA, 47 43 Bytes

Anonymous VBE immediate window function that takes input, as month name, abbreviation, or number, from range [A1] and outputs the length of that month in the year 2001 to the VBE immediate window function.

?31-Day(DateValue("1 "&[A1]&" 1")+30)Mod 31

Old Version

d=DateValue(["1 "&A1&" 1"]):?DateAdd("m",1,d)-d
\$\endgroup\$
1
\$\begingroup\$

PHP, 38 33+1 32+1 bytes

Saved 5 bytes thanks to Titus

<?=date(t,strtotime("$argn 1"));

Run as pipe with -nF

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ Hey, I don't think you need .' 1', it seems to work on TIO without it! \$\endgroup\$ – Dom Hastings Nov 2 '17 at 16:51
  • 1
    \$\begingroup\$ 28+1 bytes: <?=date(t,strtotime($argn)); (run as pipe with -nF) \$\endgroup\$ – Titus Nov 2 '17 at 21:21
  • 3
    \$\begingroup\$ @DomHastings - so, before I posted, I had tested to see if it would work without the .' 1', but it wasn't working. After seeing your comment, I tried to figure out what I had done wrong. Because I was running it on the 31st of the month, it was taking the 31st (current) day for any month I put in, which would put it beyond the current month. Feb 31st turns into March 3rd, so the code returns 31 (the number of days in March). Because of this, every month was returning 31. So, it works without the .' 1' on any day <= 28th of the month. \$\endgroup\$ – Jo. Nov 3 '17 at 3:47
  • \$\begingroup\$ Ahhh, I forget about how PHP fills in the blanks! Thanks for explaining! \$\endgroup\$ – Dom Hastings Nov 3 '17 at 5:34
  • \$\begingroup\$ @Titus Thank you. I'm such a golf newbie! I don't know why I didn't realize the 't' -> t. Also, I had to do a bunch of searching to figure out how to "run as pipe with -nF" but I got it figured out (I think). :) \$\endgroup\$ – Jo. Nov 3 '17 at 6:33
0
\$\begingroup\$

Java (OpenJDK 8), 126 bytes

s->{for(java.time.Month m:java.time.Month.values())if(m.name().startsWith(s.toUpperCase()))System.out.print(m.length(false));}

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ I think you can shorten false to a boolean expression like 1<0 to save a couple bytes. \$\endgroup\$ – Justin Mariner Oct 31 '17 at 0:53
0
\$\begingroup\$

QBIC, 49 35 bytes

?31-(instr(@aprjunsepnov feb`,;)%3)

Significantly shorter with some trickery.

Explanation

?                          PRINT
31-(                       31 minus
  instr(                   the position of
                      ,;   our input string
    @aprjunsepnov feb`  )  in the string cntaining all non-31 months                                
    %3)                    modulo 3 (this yields a 1 for each month except feb=2)
\$\endgroup\$
0
\$\begingroup\$

Perl 5, 24 bytes

23 bytes code + 1 for -p.

$_=31-/p|un|no/i-/f/i*3

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Ruby, 45 bytes

->m{((Date.parse(m)>>1)-1).day}
require'date'

Try it online!

Ruby's Date.parse accepts a month name on its own. What would normally be a right-shift (>>) actually adds to the month of the Date object. Subtraction affects the day of the month, which will wrap backwards to the last day of the previous month.

\$\endgroup\$
0
\$\begingroup\$

Kotlin, 92 bytes

val d={m:String->arrayOf(0,31,30,30,31,30,31,28,31,0,30)[(m[1].toInt()+m[2].toInt())%32%11]}

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.