11
\$\begingroup\$

A common year is a year that is not a leap year and where the first and last day of the year are on the same day. A special common year is one that starts on a Monday and so ends on a Monday as well.

Your challenge is to create a program/function that when given a year as input finds the nearest special common year, outputting itself if it is a common year. If the year is as close to the one before it as the one next to it output the larger one.

Input

An integer representing the year to test against in the range 1600 <= x <= 2100.

Output

An integer representing the nearest special common year.

Test cases

2017 -> 2018
2018 -> 2018
1992 -> 1990
1600 -> 1601
2100 -> 2103
1728 -> 1731 (lies between 1725 and 1731)

Notes

All 54 years in the given range are already shown in the linked Wikipedia article. I will also provide them here for reference:

1601, 1607, 1618, 1629, 1635, 1646, 1657, 1663, 1674, 1685, 1691
1703, 1714, 1725, 1731, 1742, 1753, 1759, 1770, 1781, 1787, 1798
1810, 1821, 1827, 1838, 1849, 1855, 1866, 1877, 1883, 1894, 1900
1906, 1917, 1923, 1934, 1945, 1951, 1962, 1973, 1979, 1990
2001, 2007, 2018, 2029, 2035, 2046, 2057, 2063, 2074, 2085, 2091
2103 (Needed for 2097 to 2100)
\$\endgroup\$
  • 1
    \$\begingroup\$ just for reference to help people out, the sequence appears to go 6, 11, 11. IE 6 years after the first is another, 11 years after that is another, 11 years after that is another, 6 years after that is another, etc. \$\endgroup\$ – Skidsdev May 24 '17 at 10:47
  • 6
    \$\begingroup\$ @Mayube Not quite. The actual sequence is "6, 11, 11, 6, 11, 11, 6, 11, 11, 6, 12, 11, 11, 6, 11, 11, 6, 11, 11, 6, 11, 12, 11, 6, 11, 11, 6, 11, 11, 6, 11, 6, 6, 11, 6, 11, 11, 6, 11, 11, 6, 11, 11, 6, 11, 11, 6, 11, 11, 6, 11, 11, 6" (note the 12s and the 6, 11, 6, 6, 11, 6) \$\endgroup\$ – Martin Ender May 24 '17 at 10:49
  • 1
    \$\begingroup\$ Since the calendar repeats every 400 years, the relevant (periodic) part of the sequence is "6, 11, 11, 6, 11, 11, 6, 11, 11, 6, 12, 11, 11, 6, 11, 11, 6, 11, 11, 6, 11, 12, 11, 6, 11, 11, 6, 11, 11, 6, 11, 6, 6, 11, 6, 11, 11, 6, 11, 11, 6, 11, 11". I'll be impressed if anyone can save bytes with this, due to the three irregularities. \$\endgroup\$ – Martin Ender May 24 '17 at 11:08
  • 5
    \$\begingroup\$ Congrats on 2k for me! :P \$\endgroup\$ – TheLethalCoder May 24 '17 at 11:38
  • 1
    \$\begingroup\$ a year that is not a leap year and where the first and last day of the year are on the same day The second part of that definition is redundant. All non-leap years start and end on the same day, being exactly 52 weeks and one day (365 days) long. \$\endgroup\$ – John Gordon May 24 '17 at 18:56

10 Answers 10

1
\$\begingroup\$

Jelly, 30 bytes

“Þıİs|9ṗ[¿¶F’ṃ“©€¿‘⁽£d;+\ạÐṂ⁸Ṁ

A monadic link taking and returning an integer year.

Try it online! or see a test suite.

How?

Much like other answers this builds the list of years required for the input domain from the increments, and finds the maximal year of minimal absolute difference from the input.

“Þıİs|9ṗ[¿¶F’ṃ“©€¿‘⁽£d;+\ạÐṂ⁸Ṁ - Main link: number y
                   ⁽£d         - augmented base 250 literal = 1601
“Þıİs|9ṗ[¿¶F’                  - base 250 literal = 20129386383114231907032071
              “©€¿‘            - code page index list = [6,12,11]
             ṃ                 - base decompression = [6,11,11,6,11,11,6,11,11,6,12,11,11,6,11,11,6,11,11,6,11,12,11,6,11,11,6,11,11,6,11,6,6,11,6,11,11,6,11,11,6,11,11,6,11,11,6,11,11,6,11,11,6,12]
                      ;        - concatenate = [1601,6,11,11,6,11,11,6,11,11,6,12,11,11,6,11,11,6,11,11,6,11,12,11,6,11,11,6,11,11,6,11,6,6,11,6,11,11,6,11,11,6,11,11,6,11,11,6,11,11,6,11,11,6,12]
                       +\      - reduce with addition = [1601,1607,1618,1629,1635,1646,1657,1663,1674,1685,1691,1703,1714,1725,1731,1742,1753,1759,1770,1781,1787,1798,1810,1821,1827,1838,1849,1855,1866,1877,1883,1894,1900,1906,1917,1923,1934,1945,1951,1962,1973,1979,1990,2001,2007,2018,2029,2035,2046,2057,2063,2074,2085,2091,2103]
                            ⁸  - link's left argument, y
                          ÐṂ   - filter keep if maximal:
                         ạ     -   absolute difference
                             Ṁ - maximum (alternatively tail, Ṫ, since increasing)
\$\endgroup\$
9
\$\begingroup\$

PHP, 67 Bytes

for(;date(LN,mktime(0,0,0,1,1,$y=$argn+$i))>1;)$i=($i<1)-$i;echo$y;

Try it online!

or

for(;date(LN,strtotime("1/1/".$y=$argn+$i))>1;)$i=($i<1)-$i;echo$y;

Try it online!

Expanded

for(;
date(LN,mktime(0,0,0,1,1,$y=$argn+$i)) # N is 1 for Monday and L is 0 for Non leap year
>1;) # loop so long as expression is not 1
  $i=($i<1)-$i; # set $i 0,1,-1,2,-2 ...
echo$y; # Output Year

date

\$\endgroup\$
  • 1
    \$\begingroup\$ Save a byte: $i=($i<1)-$i; \$\endgroup\$ – Christoph May 24 '17 at 13:21
8
\$\begingroup\$

Python 2, 129 124 118 bytes

a=[11,11,6]*13
a[29:29]=a[19:19]=12,
a[10:10]=6,6
n=input()
o=[2401-n]
for i in a*2:o+=o[-1]-i,
print n+min(o,key=abs)

Try it online! or Try all test cases
First, the sequence is generated (reversed) on a, then 2401 - input_year is used as starting value to be subtracted over the sequence.
This way the list o will contain the differences between all the common years and the input, the nearest year will be the number that is nearest to zero (positive or negative), then will be extracted with (min, key=abs) and added back to the input.

With datetime, 119 bytes

lambda i:i+min([y-i for y in range(2200,1500,-1)if datetime(y,1,1).weekday()<1and y%400],key=abs)
from datetime import*

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Does this generate the list of years based off of the sequence? \$\endgroup\$ – TheLethalCoder May 24 '17 at 12:22
  • \$\begingroup\$ @TheLethalCoder kinda, added a little explanation \$\endgroup\$ – Rod May 24 '17 at 12:24
7
\$\begingroup\$

05AB1E, 41 bytes

6xD<Š)•HΘ%A°,SΔA)u•3вè.pO0¸ì1601+DI-ÄWQϤ

Try it online!

Explanation

6xD<Š)                                     # push the list [11,6,12]
      •HΘ%A°,SΔA)u•                        # push the number 20129386383114231907032071
                   3в                      # convert to a base-3 digit list
                     è                     # use this to index into the first list
                      .p                   # get list of prefixes
                        O                  # sum each sublist
                         0¸ì               # prepend 0
                            1601+          # add 1601 to each
                                 D         # duplicate
                                  I-       # subtract input from each
                                    Ä      # calculate absolute value
                                     WQÏ   # keep only the years that have the 
                                           # smallest absolute difference from input
                                        ¤  # get the last one
\$\endgroup\$
5
\$\begingroup\$

JavaScript (ES6), 77 bytes

f=(y,z=y,d=m=>new Date(y,m,!m||31).getDay()-1)=>d(0)|d(11)?f(z<y?z-1:z+1,y):y
<input type=number value=2001 oninput=o.textContent=f(+this.value)><pre id=o>2001

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

Mathematica, 70 bytes

Max@Nearest[Select[Range[7!],!LeapYearQ@{#}&&DayName@{#}==Monday&],#]&

Generates a list of all special common years up to year 5040 (= 7!) and then finds the nearest one to the input, taking the maximum in case of a tie.

\$\endgroup\$
  • \$\begingroup\$ This was the sort of answer I was expecting, generate the list and compare to that. It would be interesting to see if anyone can use the "sequence" to find an answer. \$\endgroup\$ – TheLethalCoder May 24 '17 at 11:03
  • 4
    \$\begingroup\$ Whaaaa... PHP beats Mathematica? \$\endgroup\$ – bishop May 24 '17 at 13:41
  • \$\begingroup\$ I was playing around with your code and came up with this: (n=1;t=#;While[!DayName@{t}==Monday||LeapYearQ@{t},n++;t=#-(-1)^n*Floor[n/2]];t)& can you golf this by replacing while with //.t/; etc? I tried but I can't... \$\endgroup\$ – J42161217 May 24 '17 at 22:43
3
\$\begingroup\$

Java 7, 217 bytes

import java.util.*;int c(int y){return d(y,1);}int d(int y,int x){Calendar c=Calendar.getInstance(),d=Calendar.getInstance();c.set(y,0,1);d.set(y,11,31);return c.get(7)==d.get(7)&c.get(7)==2?y:d(y+x,x>0?-++x:-(--x));}

Explanation:

Try it here.

import java.util.*;                   // Required import for Calendar

int c(int y){                         // Method with integer parameter and integer return-type
  return d(y,1);                      //  Call second method with additional parameter
}                                     // End of method (1)

int d(int y,int x){                   // Method (2) with two integer parameters and integer return-type
  Calendar c=Calendar.getInstance(),  //  Create two Calendar instances
           d=Calendar.getInstance();
  c.set(y,0,1);                       //  Set one to 01 January yyyy
  d.set(y,11,31);                     //  and one to 31 December yyyy
  return c.get(7)==d.get(7)           //  If both are the same day of the week
         &c.get(7)==2?                //  and it is a Monday:
          y                           //   Return the input-year
         :                            //  Else:
          d(y+x,                      //   Recursive-call with year + `x`
                x>0?-++x:-(--x));     //   and change `x` to the next to check
                                      //   +1,-2,+3,-4,+5,-6,etc.
}                                     // End of method (2)
\$\endgroup\$
  • \$\begingroup\$ if x is always going to be 1, why not just remove int c(){} and change int d(int y, int x){} to d(int y){int x = 1;...} \$\endgroup\$ – Brian H. May 24 '17 at 13:27
  • \$\begingroup\$ @BrianH. Because I do a recursive-call which uses x, so if I reset it to 1 every time at the top of the method, x is incorrect and the recursive-call would fail. \$\endgroup\$ – Kevin Cruijssen May 24 '17 at 13:47
3
\$\begingroup\$

MATL, 32 31 bytes

`G@2&\wEq*-XJtQv24KUhBh9XO77-}J

Try it online! Or verify all test cases

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

C#, 183 bytes

To get the ball rolling a bit, here's an implementation I've done of my own. I'm pretty sure it can still be golfed down so if anyone wants to feel free to post as a new answer.

namespace System.Linq{n=>Enumerable.Range(1,9999).Where(y=>!DateTime.IsLeapYear(y)&(int)new DateTime(y,1,1).DayOfWeek==1).GroupBy(y=>Math.Abs(n-y)).OrderBy(g=>g.Key).First().Last();}

Try it online!

Full/Formatted version, this also shows all outputs for the given range when ran.

namespace System.Linq
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<int, int> f = n =>
                Enumerable.Range(1, 9999)
                          .Where(y => !DateTime.IsLeapYear(y)
                                    & (int)new DateTime(y, 1, 1).DayOfWeek == 1)
                          .GroupBy(y => Math.Abs(n - y))
                          .OrderBy(g => g.Key)
                          .First()
                          .Last();

            for (int y = 1600; y <= 2100; ++y)
            {
                Console.WriteLine($"{y} -> {f(y)}");
            }

            Console.ReadLine();
        }
    }
}
\$\endgroup\$
1
\$\begingroup\$

Ruby, 145 bytes

f=->i{i+(1.upto(i).map{|m|Time.new(y=i+m).monday?&&Time.new(y,6).friday?? m:Time.new(y=i-m).monday?&&Time.new(y,6).friday?? -m :nil}.find{|a|a})}

Defines a lambda taking the start year as input - f[2017] => 2018

Gotta love the Ruby standard library! wday==1 is the same length as monday? and infinitely less cool :). The special common year check is done by the fact that in a common year starting Monday, 1st June is a Friday ("friday" being the equal-shortest day name!)

Unfortunately, it's not so good at searching in both directions.

\$\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.