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\$
6
  • 21
    \$\begingroup\$ You should probably list out all the variations of the month names that we should be able to accept. \$\endgroup\$
    – Giuseppe
    Oct 28, 2017 at 17:40
  • 2
    \$\begingroup\$ For anybody who can use it, the ASCII ordinal sums of the first 3 characters lowered are unique. \$\endgroup\$ Oct 28, 2017 at 18:20
  • 21
    \$\begingroup\$ That was far, far too soon to accept a solution. \$\endgroup\$
    – Shaggy
    Oct 28, 2017 at 19:28
  • 6
    \$\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, 2017 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\$ Oct 28, 2017 at 23:27

29 Answers 29

36
\$\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\$
7
  • 15
    \$\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\$
    – hyper-neutrino
    Oct 28, 2017 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, 2017 at 18:04
  • \$\begingroup\$ substr? slice! \$\endgroup\$
    – Neil
    Oct 28, 2017 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\$
    – hyper-neutrino
    Oct 28, 2017 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, 2017 at 18:45
18
\$\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\$
4
  • \$\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, 2017 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, 2017 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\$ Oct 30, 2017 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, 2017 at 11:15
12
\$\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\$
1
6
\$\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\$
8
  • \$\begingroup\$ @Riker oh whoops that wasn't there when I started writing this :/ \$\endgroup\$
    – hyper-neutrino
    Oct 28, 2017 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\$ Oct 28, 2017 at 17:52
  • 1
    \$\begingroup\$ Nevermind changing I'm deleting that part \$\endgroup\$
    – qw3n
    Oct 28, 2017 at 17:53
  • \$\begingroup\$ I think you can use a single string 'sepaprjunnov' instead of a list of strings. \$\endgroup\$ Oct 28, 2017 at 17:55
  • \$\begingroup\$ @JonathanFrech maybe; I'll try that, thanks \$\endgroup\$
    – hyper-neutrino
    Oct 28, 2017 at 17:56
6
\$\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\$
1
  • 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, 2017 at 1:26
6
\$\begingroup\$

APL (Dyalog), 32 27 bytes (SBCS)

-2 thanks to ngn

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

32-1⌈∘⊃'.p|un|no|feb'⎕S 1⍠1

Try it online!

1⌈∘⊃ and use the maximum of one and the first element of that (0 if none) to pick from

32- thirty-two minus

1⌈∘ the maximum of one and

 the first element (0 if none) of

'.p|un|no|f'⎕S the PCRE *Search for any-char,"p" or "un" or "no" or "f"

1 returning the length of the matches

⍠1 and done case insensitively

  • Apr, Sep, Jun, and Nov will give 32−2=30

  • Feb will give 32−3=29 (allowed by OP)

  • anything else will give 32−1=31

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The programming language answering in itself. \$\endgroup\$
    – Razetime
    Aug 12, 2020 at 4:26
  • \$\begingroup\$ yeah that's impressive! \$\endgroup\$
    – matt
    Sep 19, 2020 at 2:19
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\$
7
  • 6
    \$\begingroup\$ This returns 31 for FEB. \$\endgroup\$
    – Laikoni
    Oct 29, 2017 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\$ Oct 29, 2017 at 11:20
  • 1
    \$\begingroup\$ @JonathanAllan Well, the OP has accepted this answer, so I guess it's valid? \$\endgroup\$ Oct 29, 2017 at 17:12
  • 4
    \$\begingroup\$ @EriktheOutgolfer I would not jump to that conclusion personally. \$\endgroup\$ Oct 29, 2017 at 17:41
  • \$\begingroup\$ I was under the impression that it only needed to work for one form of inputs \$\endgroup\$
    – Blue
    Oct 29, 2017 at 22:28
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\$
3
  • \$\begingroup\$ Does this work without using System;? Or can you excluse that from the byte count? \$\endgroup\$
    – Matty
    Oct 30, 2017 at 13:13
  • \$\begingroup\$ @Matty That's a good point; now added. \$\endgroup\$
    – Ayb4btu
    Oct 30, 2017 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\$ Nov 10, 2017 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\$
6
  • \$\begingroup\$ Better than mine heh \$\endgroup\$ Oct 28, 2017 at 18:11
  • 1
    \$\begingroup\$ um \$\endgroup\$ Oct 28, 2017 at 18:12
  • \$\begingroup\$ :P builtins are sometimes too long :P \$\endgroup\$
    – hyper-neutrino
    Oct 28, 2017 at 18:12
  • \$\begingroup\$ @totallyhuman oh rly wow. +1 thanks :P \$\endgroup\$
    – hyper-neutrino
    Oct 28, 2017 at 18:12
  • 2
    \$\begingroup\$ umm \$\endgroup\$ Oct 28, 2017 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\$
0
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\$

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

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

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ -6 bytes : ($_)=/.(..)/; instead of $_=substr$_,1,2; and () around "eprunov"=~/$_/i can be removed. \$\endgroup\$ Nov 3, 2017 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
  • 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, 2017 at 5:29
2
\$\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\$
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\$

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\$
2
  • \$\begingroup\$ 86 bytes. \$\endgroup\$ Oct 28, 2017 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\$ Oct 28, 2017 at 19:38
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\$
2
  • \$\begingroup\$ I believe you could save a byte by using 'f' instead of 'eb' \$\endgroup\$ Oct 28, 2017 at 18:19
  • \$\begingroup\$ 28 bytes \$\endgroup\$
    – ovs
    Oct 30, 2017 at 17:41
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\$
6
  • 1
    \$\begingroup\$ Hey, I don't think you need .' 1', it seems to work on TIO without it! \$\endgroup\$ Nov 2, 2017 at 16:51
  • 1
    \$\begingroup\$ 28+1 bytes: <?=date(t,strtotime($argn)); (run as pipe with -nF) \$\endgroup\$
    – Titus
    Nov 2, 2017 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, 2017 at 3:47
  • \$\begingroup\$ Ahhh, I forget about how PHP fills in the blanks! Thanks for explaining! \$\endgroup\$ Nov 3, 2017 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, 2017 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
  • 1
    \$\begingroup\$ I think you can shorten false to a boolean expression like 1<0 to save a couple bytes. \$\endgroup\$ Oct 31, 2017 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\$
0
\$\begingroup\$

JavaScript (V8), 100 bytes

(z,v=n=e=b=r=y=u=c=0,t=s=a=l=1,f=p=29,m=j=g=o=30,d=31)=>eval([z[0],z[1],z[2]].join`+`.toLowerCase())

Try it online!

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

Japt, 12 bytes

Uses Herman's method.

´H-ÐU+H f uH

Try it (includes all test cases, footer resets the H constant for the next case)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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