19
\$\begingroup\$

(Inspired by last week's Riddler on FiveThirtyEight.com. Sandbox post.)

Given a year between 2001 and 2099, calculate and return the number of days during that calendar year where mm * dd = yy (where yy is the 2-digit year).

2018, for example, has 5:

  • January 18th (1 * 18 = 18)
  • February 9th (2 * 9 = 18)
  • March 6th (3 * 6 = 18)
  • June 3rd (6 * 3 = 18)
  • September 2nd (9 * 2 = 18)

Input can be a 2 or 4-digit numeric year.

Output should be an integer. Optional trailing space or return is fine.

Complete input/output list:

Input = Output
 2001 = 1     2021 = 3     2041 = 0     2061 = 0     2081 = 2
 2002 = 2     2022 = 3     2042 = 4     2062 = 0     2082 = 0
 2003 = 2     2023 = 1     2043 = 0     2063 = 3     2083 = 0
 2004 = 3     2024 = 7     2044 = 3     2064 = 2     2084 = 5
 2005 = 2     2025 = 2     2045 = 3     2065 = 1     2085 = 1
 2006 = 4     2026 = 2     2046 = 1     2066 = 3     2086 = 0
 2007 = 2     2027 = 3     2047 = 0     2067 = 0     2087 = 1
 2008 = 4     2028 = 4     2048 = 6     2068 = 1     2088 = 3
 2009 = 3     2029 = 1     2049 = 1     2069 = 1     2089 = 0
 2010 = 4     2030 = 6     2050 = 3     2070 = 3     2090 = 5
 2011 = 2     2031 = 1     2051 = 1     2071 = 0     2091 = 1
 2012 = 6     2032 = 3     2052 = 2     2072 = 6     2092 = 1
 2013 = 1     2033 = 2     2053 = 0     2073 = 0     2093 = 1
 2014 = 3     2034 = 1     2054 = 4     2074 = 0     2094 = 0
 2015 = 3     2035 = 2     2055 = 2     2075 = 2     2095 = 1
 2016 = 4     2036 = 6     2056 = 4     2076 = 1     2096 = 4
 2017 = 1     2037 = 0     2057 = 1     2077 = 2     2097 = 0
 2018 = 5     2038 = 1     2058 = 0     2078 = 2     2098 = 1
 2019 = 1     2039 = 1     2059 = 0     2079 = 0     2099 = 2
 2020 = 5     2040 = 5     2060 = 6     2080 = 4

This is a challenge, lowest byte count in each language wins.

Pre-calculating and simply looking up the answers is normally excluded per our loophole rules, but I'm explicitly allowing it for this challenge. It allows for some interesting alternate strategies, although its not likely a 98 99-item lookup list is going to be shortest.

\$\endgroup\$
  • \$\begingroup\$ If it makes it any easier in your language, the answer will be the same regardless of century; 1924 and 2124 have the same number of days as 2024. \$\endgroup\$ – BradC Apr 13 '18 at 14:41
  • \$\begingroup\$ if the the result of mm*dd is bigger than 100 it is automatically filtered? \$\endgroup\$ – DanielIndie Apr 13 '18 at 16:03
  • \$\begingroup\$ @DanielIndie Correct, no "wraparound" dates should be counted. In other words, Dec 12, 2044 doesn't count, even though 12 * 12 = 144. \$\endgroup\$ – BradC Apr 13 '18 at 16:13
  • \$\begingroup\$ As we need only handle a limited number of inputs, I've edited them all in. Feel free to rollback or reformat. \$\endgroup\$ – Shaggy Apr 13 '18 at 17:20
  • 1
    \$\begingroup\$ @gwaugh Just that you can decide which to accept as valid input (so you don't have to spend extra characters converting between the two). \$\endgroup\$ – BradC Feb 4 at 20:06

31 Answers 31

14
\$\begingroup\$

Excel, 48 bytes

Hooray! Finally something Excel is actually good at.

=COUNT(VALUE(ROW(1:12)&"/"&A1/ROW(1:12)&"/"&A1))

Takes input from A1 in the form of an integer 1-99 representing the year, and outputs to wherever you enter this formula. It's an array formula, so use Ctrl-Shift-Enter instead of Enter to enter it.

This takes advantage of the fact that COUNT ignores errors, so any errors that are caused by either the month not dividing the year (leading Excel to parse something like 2/12.5/25 or by the date not being valid, like 2/29/58, are just silently ignored.

\$\endgroup\$
  • 1
    \$\begingroup\$ Very nice. Probably worth mentioning it requires a 2-digit year in A1. Entering a 4-digit year simply returns 0. \$\endgroup\$ – BradC Apr 13 '18 at 19:14
  • \$\begingroup\$ True! I'll edit that into the description. \$\endgroup\$ – Sophia Lechner Apr 13 '18 at 19:16
  • \$\begingroup\$ Also I should mention that it's locale-specific; it depends on having a locale that uses a mm/dd/yy ordering. In a locale with dd/mm/yy ordering, the answer would be the same number of bytes, of course. \$\endgroup\$ – Sophia Lechner Apr 13 '18 at 19:20
  • 1
    \$\begingroup\$ Super clever; I like how you're only testing 12 date candidates (one per month), instead of running through every day of the year. \$\endgroup\$ – BradC Apr 13 '18 at 19:26
  • \$\begingroup\$ Fix the year to a non-leap one save byte? \$\endgroup\$ – l4m2 May 4 '18 at 2:59
6
\$\begingroup\$

Python 2, 44 bytes

[k/32%13*(k%32)for k in range(96,509)].count

Try it online!

An anonymous function given as a method object. Produces all products of (month, day) pairs (m, d) as encoded by k=32*m+d with 0≤m≤12, 0≤d≤31, wrapping around. Eliminates Feb 29-31 by excluding them from the range.

\$\endgroup\$
5
\$\begingroup\$

Java (JDK 10), 65 bytes

y->{int m=13,c=0;for(;m-->1;)if(y%m<1&y/m<29+m%2*3)c++;return c;}

Try it online!

Credits

\$\endgroup\$
  • \$\begingroup\$ No leap year fits 29*n, so no need the check \$\endgroup\$ – l4m2 Apr 13 '18 at 16:36
  • \$\begingroup\$ Thanks for your input. I could remove 27 bytes in total with a bunch of other changes. \$\endgroup\$ – Olivier Grégoire Apr 13 '18 at 18:07
  • 1
    \$\begingroup\$ Changing (m==2?29:32) to 29+m%2*3 still seems to give all OK results. Credit to @AsoneTuhid's Ruby answer. \$\endgroup\$ – Kevin Cruijssen Apr 16 '18 at 6:52
4
\$\begingroup\$

PowerShell, 94 bytes

param($a)for($x=Date 1/1/$a;$x-le(Date 12/9/$a);$x=$x.AddDays(1)){$z+=$x.Month*$x.Day-eq$a};$z

Try it online!

Takes input as a two-digit year, then constructs a for loop from 1/1/year to 12/9/year (because 12/10 and beyond will never count and this saves a byte). Each iteration, we increment $z iff the .Month times the .Day is equal to our input year. Outside the loop, $z is left on the pipeline and output is implicit.

Edit - this is culture dependent. The above code works for en-us. The date format may need to change for other cultures.

\$\endgroup\$
  • 2
    \$\begingroup\$ "culture"? Did you mean "locale"? ... \$\endgroup\$ – user202729 Apr 13 '18 at 15:31
  • 1
    \$\begingroup\$ @user202729 Colloquially, yes, but PowerShell documentation refers to it as "culture." \$\endgroup\$ – AdmBorkBork Apr 13 '18 at 15:33
  • \$\begingroup\$ "Culture" is the word MS generally uses to talk about locale, eg. System.Globalization.CultureInfo in .NET. \$\endgroup\$ – sundar Jun 29 '18 at 18:31
4
\$\begingroup\$

Ruby, 46 42 bytes

->y{(1..12).count{|j|y%j<1&&y/j<29+j%2*3}}

Try it online!

\$\endgroup\$
4
\$\begingroup\$

JavaScript (Node.js), 48 44 43 bytes

F=(x,h)=>h>12?0:F(x,-~h)+(x/h<3*h%6+28>x%h)

Try it online!

JavaScript (Node.js), 59 58 bytes

x=>[a=0,...'030101001010'].map((k,m)=>x%m|x/m>31-k||a++)|a

Try it online!

\$\endgroup\$
  • \$\begingroup\$ I knew a mathematical solution would be short, but I had fun with my version :) \$\endgroup\$ – Shaggy Apr 13 '18 at 16:48
3
\$\begingroup\$

Jelly, 15 bytes

31x12_2¦3RĖP€Fċ

Try it online!

Take a number in range [0,100[ as input.

\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 91 bytes

I was curious to know how hardcoding would compare to an iterative computation. It's definitely longer (see @Shaggy's answer), but not awfully longer.

Edit: It is, however, much longer than a more direct formula (see @l4m2 answer).

Takes input as an integer in [1..99].

n=>(k=parseInt('8ijkskercdtbnqcejh6954r1eb2kc06oa3936gh2k0d83d984h'[n>>1],36),n&1?k>>3:k&7)

Try it online!

How?

Odd years have significantly less chance of having mm * dd = yy than even years. More concretely, odd years have 0 to 3 matches, while even years have 0 to 7 matches. This allows us to code each pair of years with just 5 bits, which can conveniently be represented as a single character in base 36.

\$\endgroup\$
3
\$\begingroup\$

Perl 6, 40 bytes

{+grep {$_%%$^m&&29+$m%2*3>$_/$m},1..12}

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Python 2 and 3, 55 52 bytes

lambda y:sum(y%i<(y/i<29+i%2*3)for i in range(1,13))

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Bash + GNU utilities, 57

  • 1 byte saved thanks to @SophiaLechner
seq -f1/1/$1+%gday 0 365|date -f- +%m*%d-%y|bc|grep -c ^0

Note that the seq command always produces a list of 366 dates - for non-leap years January 1st of the next year will be included. However in the date range 2001..2099, MM*DD will never be YY for January 1st of the next year for any of these years, so this extra day won't affect the result.

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Very nice - I hadn't even known date will do date math like that while parsing. seq doesn't need a space after the -f, so you can save a byte there. \$\endgroup\$ – Sophia Lechner Apr 16 '18 at 17:22
3
\$\begingroup\$

T-SQL, 123 121 bytes

Per our IO rules, input is taken via pre-existing table t with an integer field y, which contains a 2-digit year.

WITH c AS(SELECT 1m UNION ALL SELECT m+1FROM c WHERE m<12)
SELECT SUM(ISDATE(CONCAT(m,'/',y/m,'/',y)))FROM c,t WHERE y%m=0

Line break is for readability only. Inspired largely by Sophia's Excel solution.

  • The top line generates a 12-item number table c which is joined to the input table t.
  • If that passes I mash together a date candidate using CONCAT(), which does implicit varchar datatype conversions. Otherwise I'd have to do a bunch of CAST or CONVERT statements.
  • Found the perfect evaluation function ISDATE(), which returns 1 for valid dates and 0 for invalid dates.
  • Wrap it in a SUM and I'm done.
  • EDIT: Moved the integer division check (y%m=0) into the WHERE clause to save 2 bytes, thanks @RazvanSocol.

Unfortunately, it's not much shorter than the lookup table version (using the string from osdavison's version):

T-SQL lookup, 129 bytes

SELECT SUBSTRING('122324243426133415153317223416132126011504033106131204241006003213011306002122042005101305111014012',y,1)FROM t

EDIT: Leaving my original above, but we can save a few bytes by using a couple of new functions:

  • STRING_SPLIT is available in MS SQL 2016 and above.
  • CONCAT_WS is available in MS SQL 2017 and above.
  • As above, replaced IIF with WHERE

MS-SQL 2017, 121 118 bytes

SELECT SUM(ISDATE(CONCAT_WS('/',value,y/value,y)))
FROM t,STRING_SPLIT('1-2-3-4-5-6-7-8-9-10-11-12','-')
WHERE y%value=0

MS-SQL 2017, extra cheaty edition: 109 bytes

SELECT SUM(ISDATE(CONCAT_WS('/',number,y/number,y)))
FROM t,spt_values WHERE number>0AND y%number=0AND'P'=TYPE

Requires you to be in the master database, which contains a system table spt_values that (when filtered by TYPE='P'), gives you counting numbers from 0 to 2048.

\$\endgroup\$
  • \$\begingroup\$ Similar to other answers, the order I assemble the date (m/d/y) depends on the locality settings of the SQL instance. Other localities might require a different order, or a different separator, but I don't think would impact the code length. \$\endgroup\$ – BradC Apr 13 '18 at 21:47
  • \$\begingroup\$ I guess you realized that converting '2/29/64' to a date gives 1964-02-29 (not 2064-02-29), but considering that the years 2000 and 2100 are excluded, it's a nice way to get a shorter code. \$\endgroup\$ – Razvan Socol Apr 15 '18 at 9:45
  • \$\begingroup\$ Using SPLIT_STRING instead of a CTE brings it down to 120 bytes. Using CONCAT_WS instead of CONCAT saves another character, getting it to 119 bytes. \$\endgroup\$ – Razvan Socol Apr 15 '18 at 9:51
  • \$\begingroup\$ @RazvanSocol Yeah, I'm not sure where the break is between 2-digit dates that map to 19xx vs 20xx, but both give the same answer. I'll give the other two suggestions a try, thanks! \$\endgroup\$ – BradC Apr 15 '18 at 18:08
  • 1
    \$\begingroup\$ Replace IIF with WHERE. \$\endgroup\$ – Razvan Socol Apr 17 '18 at 19:24
3
\$\begingroup\$

Julia 0.6, 49 44 42 bytes

y->sum(y/i∈1:28+3(i%2⊻i÷8)for i=1:12)

Try it online!

-5 bytes inspired by Asone Tuhid's Ruby answer.
-2 bytes replacing count with sum

Explanation:

For each month i from 1 to 12, calculate y/i, and check if it's one of that month's days. Months with 31 days are 1, 3, 5, 7, 8, 10, 12 - so they're odd below 8 and even at and above 8. So either i%2 or i÷8 (which is 0 for i<8 and 1 for i>=8 here) should be 1, but not both - so we XOR them. If the xor result is true, we check dates 1:28+3 i.e. 1:31, otherwise we check just dates 1:28.

1:28 is sufficient for the rest of the months (this improvement inspired by Asone Tuhid's Ruby answer) because:

  • for February, the only possibility would have been 2*29 = 58, but 2058 is not a leap year, so we can assume Feb always has 28 days.

  • the other months with 30 days are month 4 and above - for which i*29 (and i*30) would be above 100, which can be ignored.

Finally, we count out the number of times y/i belongs in this list of days (by using boolean sum here), and return that.

\$\endgroup\$
3
\$\begingroup\$

JavaScript, 91 85 82 81 77 bytes

Takes input as a 2-digit string (or a 1 or 2 digit integer).

Takes advantage of the fact that new Date will rollover to the next month, and continue doing so, if you pass it a day value that exceeds the number of days in the month you pass to it so, on the first iteration, it tries to construct the date yyyy-01-345 which becomes yyyy-12-11, or yyyy-12-10 on leap years. We don't need to check dates after that as 12*11+ results in a 3-digit number.

y=>(g=d=>d&&([,M,D]=new Date(y,0,d).toJSON().split(/\D/),D*M==y)+g(--d))(345)

3 bytes saved thanks to Arnauld.


Test It

f=
y=>(g=d=>d&&([,M,D]=new Date(y,0,d).toJSON().split(/\D/),D*M==y)+g(--d))(345)
o.innerText=[...Array(99)].map((_,x)=>(2001+x++)+` = `+f(x)).join`\n`
pre{column-count:5;width:480px;}
<pre id=o></pre>

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

Python 2, 89 84 68 58 bytes

f=lambda y,d=62:d-59and((d/31+1)*(d%31+1)==y)+f(y,-~d%372)

Try it online!

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

Excel, 83 bytes

{=SUM(IF(MONTH(DATE(A1,1,0)+ROW(1:366))*DAY(DATE(A1,1,0)+ROW(1:366))=A1-2000,1,0))}

Input is in cell A1 in the format yyyy. This is an array formula and is entered with Ctrl+Shift+Enter to get the curly brackets {}. It is fairly straightforward and without any cleverness.

When in an array formula, DATE(A1,1,0)+ROW(1:366) gives us an array of 366 date values. On non-leap years, this will include Jan 1 of the next year but that is not a problem because 1*1=1 and would only count as a false positive if the next year is 2001 but, since the required year range is 2001 - 2099, it will never arise as an issue.

If you shorted that bit into simply ~, the formula because much easier to follow:

{=SUM(IF(MONTH(~)*DAY(~)=A1-2000,1,0))}

I tried using COUNTIF() instead of SUM(IF()) but Excel wouldn't even let me enter it as an array formula, much less give me a result. I did find a Google Sheets solution using CountIf() but the same method otherwise that turned out to be 91 bytes, mostly because it uses ArrayFormula() instead of simply { }.

=CountIf(ArrayFormula(Month(Date(A1,1,0)+Row(1:366))*Day(Date(A1,1,0)+Row(1:366))),A1-2000)
\$\endgroup\$
  • \$\begingroup\$ I haven't seen a consensus, but I've generally not been including the outer curly brackets in my byte counts for Excel. They feel more like a way that Excel is formatting its display than part of the formula. Opinions? \$\endgroup\$ – Sophia Lechner Apr 13 '18 at 19:15
  • \$\begingroup\$ @SophiaLechner I've included them instead of deciding how to include the extra keystrokes required to enter it as an array formula. There's a meta question about that and the only answer says that the CTRL+ALT+ENTER command would count as 1 keystroke. If you use the same as vim (per meta), then that keystroke would count as 1 byte. However, I don't usually count the ENTER at the end of entering a formula as 1 byte in other answers. \$\endgroup\$ – Engineer Toast Apr 13 '18 at 21:20
2
\$\begingroup\$

Retina 0.8.2, 55 bytes

..
$*
(?<=^(1{1,12}))(?=(?(?<=^11$)\1{0,27}|\1{0,30})$)

Try it online! Takes a two-digit year; add 1 byte to support 4-digit years. Explanation: The first stage simply converts to unary. The second stage starts by matching 1 to 12 characters before the match position, representing the month, and then attempts to look ahead for a whole number of repetitions of that month. However, the lookahead contains a conditional, which chooses between up to 27 or 30 further repetitions depending on the month. The count of match positions is then the desired result.

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

R, 22 122 bytes

x=scan();substr("122324243426133415153317223416132126011504033106131204241006003213011306002122042005101305111014012",x,x)

Try it online!

Decided to go with a lookup table approach. Input year needs to be 2 digits.

\$\endgroup\$
  • 1
    \$\begingroup\$ Also on TIO you can hit the "link" button and it will auto-format the answer for you. Thanks to Dennis for setting it up! \$\endgroup\$ – Giuseppe Jul 19 '18 at 23:51
  • \$\begingroup\$ You can remove the initial if, since input can be either 2-digit or 4-digit, your choice (so you can choose to accept only 2-digit input). But it looks like the code considers every month to contain 31 days, so for eg., 62 (for 2062) returns 1 where it should return 0. \$\endgroup\$ – sundar Jul 20 '18 at 13:16
  • 2
    \$\begingroup\$ I misunderstood. That being said, I'm pretty sure the lookup table would have to be included in the byte count. \$\endgroup\$ – ngm Jul 20 '18 at 16:46
  • \$\begingroup\$ @ngm I also was not sure if the lookup table needed to be included, but I will add it to the byte count just to be safe. \$\endgroup\$ – Robert S. Jul 20 '18 at 16:51
  • \$\begingroup\$ I'm still not sure what all the rules are! \$\endgroup\$ – ngm Jul 20 '18 at 16:53
2
\$\begingroup\$

C (gcc), 65 60 59 bytes

d(a,t,e){for(t=0,e=13;e-->1;)t+=a%e<1&a/e<(e-2?32:29);a=t;}

Port of user202729's Java answer. Try it online here. Thanks to Jonathan Frech for golfing 1 byte.

\$\endgroup\$
  • \$\begingroup\$ a=0,m=13;for(; ~> for(a=0,m=13;. \$\endgroup\$ – Jonathan Frech Jun 30 '18 at 12:23
  • \$\begingroup\$ @JonathanFrech Thanks for pointing that out, I have edited. \$\endgroup\$ – O.O.Balance Jun 30 '18 at 14:56
2
\$\begingroup\$

J, 29 bytes

1#.(,x*i."+29+3*2~:x=.i.13)=]

Try it online!

How it works

1#.(,x*i."+29+3*2~:x=.i.13)=]    Input: year (2-digit, 1..99)
                   x=.i.13       array of 0..12; assign to x
                2~:              1 1 0 1 .. 1
              3*                 3 3 0 3 .. 3
           29+                   32 32 29 32 .. 32
       i."+                      for each item of above, generate a row 0 .. n
    ,x*                          each row times 0 .. 12 and flatten
                           =]    1 for each item == input, 0 otherwise
1#.                              sum

Tried hard to get under 2 times Jelly solution :)

Side note

If someone really wants to hardcode the 99-digit data, here's a bit of information:

Divide the 99-digit into chunks of 2 digits. Then the first digit is <4 and the second <8, which means five bits can encode two numbers. Then the entire data can be encoded in 250 bits, or 32 bytes.

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

Python 3, 158 162 215 241 bytes

Removed 4 Thanks to Stephen for golfing the conditionals.

Removed 53 thanks Stephen for pointing out the white space

Removed 26 thanks to the link provided by caird

I'm pretty new at this. Couldn't think of how to do this without having the days in a month be described.

r=range
def n(Y):
 a,b,c=31,30,0
 for x in r(12):
  for y in r([a,(28if Y%4else 29),a,b,a,b,a,a,b,a,b,a][x]):
   if(x+1)*(y+1)==int(str(Y)[2:]):c+=1
 return c

Try it online!

\$\endgroup\$
  • 5
    \$\begingroup\$ Welcome to the site, and nice first post! There are a fair few ways you can golf this, such as removing some whitespace, so be sure to check out these tips for golfing in Python \$\endgroup\$ – caird coinheringaahing Apr 13 '18 at 18:03
  • \$\begingroup\$ @cairdcoinheringaahing Thanks for the advice the link was very useful! \$\endgroup\$ – akozi Apr 13 '18 at 21:16
  • 1
    \$\begingroup\$ 158 bytes - golfed some away, but mostly you had a big long line of spaces on your third line, dunno how those got there \$\endgroup\$ – Stephen Feb 3 at 18:08
  • \$\begingroup\$ @Stephen Thanks :) I added yours as two edits. One for the white space and the other as the golfing. \$\endgroup\$ – akozi Feb 3 at 18:17
  • \$\begingroup\$ (28if Y%4else 29) can be shortened to [29,28][Y%4>0]. Also, the long list can be shortened to [a,...]+2*[a,b,a,b,a]. a,b,c can be added in the parameter list to save a line. int(str(Y)[2:]) can be shortened to Y%100. Finally, counter variables can mostly be shortened to lens of list comprehensions, this also allows n to be made a lambda. This makes 118. \$\endgroup\$ – Black Owl Kai Feb 4 at 0:19
2
\$\begingroup\$

Forth (gforth), 60 59 bytes

: f 0 13 1 do over i /mod swap 32 i 2 = 3 * + 0 d< - loop ;

Try it online!

This version takes advantage of the fact that there can't be more than 1 matching day per month, and that the year has to be divisible by the month for it to match.

Explanation

Iterates over the months, checks if year is divisible by month, and if the quotient is < 31 (28 for Feb) Months after March can't match for days greater than 25, so we can just assume all months (other than Feb) have 31 days for the purpose of the puzzle.

Code Explanation

: f                   \ start new word definition
  0                   \ set up counter
  13 1 do             \ start a counted loop from 1 to 12
    over i /mod       \ get the quotient and remainder of dividing year by month
    swap              \ default order is remainder-quotient, but we want to swap them
    32 i 2= 3 * +     \ we want to compare to 32 days unless month is Feb
    0 d<=             \ treat the 4 numbers on the stack as 2 double-length numbers
                      \ and compare [1]
    -                 \ subtract result from counter (-1 is true in Forth)
  loop                \ end loop
;                     \ end word definition    

[1] - Forth has the concept of double length numbers, which are stored on the stack as two single-length numbers (of the form x y, where the value of the double = y * 2^(l) + xwhere l is the size in bits of a single in the forth implementation you're working with).

In this case, I compared the quotient and remainder to 32 (or 29) 0. If the remainder was greater than 0 (year not divisble by month), the first double would be automatically bigger than 32 (or 29) 0 and the result would be false. If the remainder is 0, then it resolves to effectively a regular check of quotient <= 32 (or 29)

Forth (gforth), 61 bytes

: f 0 13 1 do i 2 = 3 * 32 + 1 do over i j * = - loop loop ; 

Try it online!

Saved some bytes by realizing that only February matters in terms of having the correct number of days in the month

Explanation

Forth (at least gforth) comparisons return -1 for true and 0 for false

0                     \ place 0 on top of the stack to use as a counter
13 1 do               \ begin the outer loop from 1 to 12
  i 2 = 3 *           \ if i is 2 place -3 on top of the stack, otherwise 0
  32 + 1 do           \ begin the inner loop from 1 to 31 (or 28)
    over              \ copy the year from stack position 2 and place it on top of the stack
    i j * = -         \ multiply month and day compare to year, subtract result from counter 
  loop                \ end the inner loop
loop                  \ end the outer loop
\$\endgroup\$
1
\$\begingroup\$

Java (JDK 10), 79 72 70 bytes

y->{int a=0,m=13;for(;m-->1;)a+=y%m<1&&y/m<(m==2?29:32)?1:0;return a;}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ @Shaggy Thanks! (leftover from previous version where I loop over all days) \$\endgroup\$ – user202729 Apr 13 '18 at 16:24
  • \$\begingroup\$ If you change && to & it's the same answer as OlivierGrégoire's Java answer, although he has answered 19 minutes earlier. \$\endgroup\$ – Kevin Cruijssen Apr 16 '18 at 6:47
  • \$\begingroup\$ @KevinCruijssen Random coincidence. Although my version is a bit worse (unnecessary use of ternary). \$\endgroup\$ – user202729 Apr 16 '18 at 11:30
1
\$\begingroup\$

JavaScript (Node.js), 108 bytes

a=>'0122324243426133415153317223416132126011504033106131204241006003213011306002122042005101305111014012'[a]

f=a=>'0122324243426133415153317223416132126011504033106131204241006003213011306002122042005101305111014012'[a]
o.innerText=[...Array(99)].map((_,x)=>(2001+x++)+` = `+f(x)).join`\n`
pre{column-count:5;width:480px;}
<pre id=o></pre>

\$\endgroup\$
  • \$\begingroup\$ Brute-force lookup, nice! \$\endgroup\$ – BradC Apr 13 '18 at 20:07
  • \$\begingroup\$ Welcome to PPCG! \$\endgroup\$ – Shaggy Apr 13 '18 at 21:27
1
\$\begingroup\$

Perl 5, 68 bytes

//;map$\+=$'%++$m==0&&$'/$m<$_,32,29,32,31,32,31,32,32,31,32,31,32}{

Try it online!

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

Python 3, 132 bytes

This is really quite long of a program but I thought it might be of interest.

All the values are between 0-7 so I encode each number with 3 bits in a long binary string. I tried pasting a raw binary string into my python program but I couldn't get it to work, so I settled on base64 in the file.

I used the following string as a lookup table (ending 7 used for padding): 01223242434261334151533172234161321260115040331061312042410060032130113060021220420051013051110140127

The program takes this string and decodes it as a number, then uses bit shifting to extract the result.

import base64
lambda n:int.from_bytes(base64.b64decode(b'pNRRxYtw01s9Jw4tFYE0QNkYsoRQgYBosEsYBFIRAUgsUkgwFQM='),'big')>>(6306-3*n)&7

66 bytes + 37 byte file = 103 bytes

This reads a binary file called e and avoids using base64.

lambda n:int.from_bytes(open('e','rb').read(),'big')>>(6306-3*n)&7

Here is a hexdump of the read file (without padding):

00000000  a4 d4 51 c5 8b 70 d3 5b  3d 27 0e 2d 15 81 34 40  |..Q..p.[='.-..4@|
00000010  d9 18 b2 84 50 81 80 68  b0 4b 18 04 52 11 01 48  |....P..h.K..R..H|
00000020  2c 52 48 30 0a                                    |,RH0.|
\$\endgroup\$
1
\$\begingroup\$

Haskell, 61 51 bytes

f y=sum[1|i<-[96..508],mod(div i 32)13*mod i 32==y]

Try it online!

Inspired by xnor's Python 2 answer and Laikoni.

\$\endgroup\$
  • 1
    \$\begingroup\$ 52 bytes: f y=sum[1|i<-[1..12],mod y i<1,div y i<29+mod i 2*3] Try it online! \$\endgroup\$ – Laikoni Apr 14 '18 at 11:04
1
\$\begingroup\$

Oracle SQL, 115 bytes

select count(decode(sign(decode(l,2,29,32)-y/l),1,1))from t,
xmltable('1to 12'columns l int path'.')where mod(y,l)=0

We may note that it does not really matter how many days in April (and later months), since 100/4 < 28. Also it's not necessary to check whether year is leap or not. We just have to specify that there are 28 days in February (not 29 because that validation will be executed only for 2058 which is not leap) otherwise it may be just 31 for any month.

Other approaches

Oracle SQL (12c Release 2 and later), 151 bytes

select count(to_date(y/l||'-'||l||'-1' default '' on conversion error,'dd-mm-y'))from t,
(select level l from dual connect by level<=12)where mod(y,l)=0

Oracle SQL (12c Release 2 and later), 137 bytes

select sum(validate_conversion(y/l||'-'||l||'-1'as date,'dd-mm-y'))from t,
(select level l from dual connect by level<=12)where mod(y,l)=0

Both solution could have been 8 bytes shorter if we replace (select level l from dual connect by level<=12) with xmltable('1to 12'columns l int path'.') but Oracle throws an exception because of the bug (tested on versions 12.2.0.1.0, 18.3.0.0.0).

SQL> select count(to_date(y/l||'-'||l||'-1' default '' on conversion error,'dd-mm-y'))from t,
  2  xmltable('1to 12'columns l int path'.')where mod(y,l)=0
  3  /
select count(to_date(y/l||'-'||l||'-1' default '' on conversion error,'dd-mm-y'))from t,
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected DATE got DATE


SQL> select sum(validate_conversion(y/l||'-'||l||'-1'as date,'dd-mm-y'))from t,
  2  xmltable('1to 12'columns l int path'.')where mod(y,l)=0
  3  /
select sum(validate_conversion(y/l||'-'||l||'-1'as date,'dd-mm-y'))from t,
*
ERROR at line 1:
ORA-43909: invalid input data type

The only case in both solution when year does matter is 2058 which is non-leap so literal '-1' was used to specify non-leap year.

Oracle SQL, 128 bytes

select count(d)from t,(select date'1-1-1'+level-1 d from dual connect by level<365)
where to_char(d(+),'mm')*to_char(d(+),'dd')=y

Oracle SQL, 126 bytes

select substr('122324243426133415153317223416132126011504033106131204241006003213011306002122042005101305111014012',y,1)from t

Update

Oracle SQL, 110 bytes

select-sum(least(sign(y/l-decode(l,2,29,32)),0))from t,
xmltable('1to 12'columns l int path'.')where mod(y,l)=0

Oracle SQL, 108 bytes

select sum(decode(sign(decode(l,2,29,32)-y/l)*mod(y+1,l),1,1))from t,
xmltable('1to 12'columns l int path'.')

Spark SQL, 137 bytes

select count(cast(regexp_replace(concat_ws('-','2001',l,y/l),'.0$','')as date))
from(select explode(array(1,2,3,4,5,6,7,8,9,10,11,12))l),t

Spark 2.3+ SQL, 126 bytes

(replace function becomes available)

select count(cast(replace(concat_ws('-','2001',l,y/l),'.0')as date))
from(select explode(array(1,2,3,4,5,6,7,8,9,10,11,12))l),t
\$\endgroup\$
1
\$\begingroup\$

PHP, 73 bytes

Using pipe input and php -nR:

for(;++$m<13;$d=1)while($d<25+($m-2?$m>4?:7:4))$c+=$argn==$m*$d++;echo$c;

Try it online!

PHP, 76 bytes

Using command line arg input php dm.php 18:

for(;++$m<13;$d=1)while($d<25+($m-2?$m>4?:7:4))$c+=$argv[1]==$m*$d++;echo$c;

Try it online!

Iterative approach. Since the only leap year to look at is 2 * 29 = 58, and 2058 is not a leap year there's no need to consider leap year in the Feb days. And since wraparound is not a concern -- from April on, any day greater than 25 will exceed 100 we just say the rest of the months only have 25 days.

Input is 2 digit year via command line (-10 bytes as program, thx to suggestion from @Titus).

OR:

PHP, 101 bytes

$y=$argv[1];for($d=strtotime("$y-1");date(Y,$d)==$y;$d+=86400)eval(date("$\x+=j*n=='y';",$d));echo$x;

Try it online!

Still iterative but using PHP's timestamp functions. Accepts year as four digit number. Thx to @Titus for suggestion of using strtotime() instead of mktime().

\$\endgroup\$
  • \$\begingroup\$ First version fails for 4-digit years, second fails for two-digit. But nice thinking. Try $m<5?$m-2?31:28:25 for the first and $d=strtotime("$y-1") for the second \$\endgroup\$ – Titus Feb 4 at 18:30
  • \$\begingroup\$ Uhm ... Why did you put the y in the eval in quotes? \$\endgroup\$ – Titus Feb 4 at 18:37
  • \$\begingroup\$ @Titus because PHP7 now treats digits preceeded with 0 as octal, so 08 and 09 are actually not valid. tio.run/##K8go@G9jXwAkU5Mz8hUMFWxtFQwsrP//BwA Using the date() function you can only get a 2 digit, zero-padded year. \$\endgroup\$ – gwaugh Feb 4 at 18:39
  • \$\begingroup\$ Bummer! of course. \$\endgroup\$ – Titus Feb 4 at 18:41
  • 1
    \$\begingroup\$ @Titus, updated second version using strtotime() instead of mktime() and re-implemented as program, -7 bytes. Also, I looked at the majority of the submissions, including the top voted ones, will only accept year as either 2 or 4 digits, so I'm going to take that to mean it is up to submitter. Thx again for the suggestions! \$\endgroup\$ – gwaugh Feb 4 at 19:56
0
\$\begingroup\$

PHP, 74 70 bytes

while(++$m<13)for($d=$m<5?$m-2?31:28:25;$d;)$n+=$d--*$m==$argn;echo$n;

accepts two-digit years only.

I adopted gwaugh´s considerations and golfed them; my 1st approach was longer than his (92 bytes):

for($y=$argn%100;++$m<13;)for($d=$m-2?30+($m+$m/8)%2:29-($y%4>0);$d;)$n+=$d--*$m==$y;echo$n;

%100 allows to use 4-digit years.


Run as pipe with -nR or try them 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.