10
\$\begingroup\$

Input

The input will be a year between 1583 and 2239. We just want to know when Hannukah was or will be in that year. This is information used by millions of people every year and put in calendars across the world. What could be easier?

Output

The Gregorian date of the first evening of Hannukah that year. That is the day before the first full day of Hannukah. Your code should output the month and day of the month in any easy to understand human readable form of your choice.

Examples

1583    December 9
2013    November 27 
2014    December 16
2015    December 6  
2016    December 24 
2017    December 12 
2018    December 2  
2019    December 22
2020    December 10 
2021    November 28 
2022    December 18
2023    December 7  
2024    December 25 
2025    December 14 
2026    December 4  
2027    December 24
2028    December 12 
2029    December 1  
2030    December 20 
2031    December 9  
2032    November 27 
2033    December 16
2239    December 21

It could hardly be simpler. We start with a couple of definitions:

We define a new inline notation for the division remainder of \$x\$ when divided by \$y\$: $$(x|y)=x \bmod y$$

For any year Gregorian year \$y\$, the Golden Number, $$G(y) = (y|19) + 1$$ For example, \$G(1996)=2\$ because \$(1996|19)=1\$.

To find \$H(y)\$, the first evening of Hannukah in the year \$y\$, we need to find \$R(y)\$ and \$R(y+1)\$, the day of September where Rosh Hashanah falls in \$y\$ and in \$y+1\$. Note that September \$n\$ where \$n≥31\$ is actually October \$n-30\$.

$$R(y)=⌊N(y)⌋ + P(y)$$ where \$⌊x⌋\$ denotes \$x-(x|1)\$, the integer part of \$x\$, and

$$N(y)= \Bigl \lfloor \frac{y}{100} \Bigr \rfloor - \Bigl \lfloor \frac{y}{400} \Bigr \rfloor - 2 + \frac{765433}{492480}\big(12G(y)|19\big) + \frac{(y|4)}4 - \frac{313y+89081}{98496}$$

We define \$D_y(n)\$ as the day of the week (with Sunday being \$0\$) that September \$n\$ falls on in the year \$y\$. Further, Rosh Hashanah has to be postponed by a number of days which is

$$P(y)=\begin{cases} 1, & \text{if } D_y\big(\lfloor N(y)\rfloor \big)\in\{0,3,5\} & (1)\\ 1, & \text{if } D_y\big(\lfloor N(y)\rfloor\big)=1 \text{ and } (N(y)|1)≥\frac{23269}{25920} \text{ and } \big(12G(y)|19\big)>11 & (2)\\ 2, & \text{if } D_y\big(\lfloor N(y)\rfloor \big)=2 \text{ and } (N(y)|1)≥\frac{1367}{2160} \text{ and } (12G(y)|19)>6 & (3)\\ 0, & \text{otherwise} & (4) \end{cases}$$

For example, in \$y=1996\$, \$G(y)=2\$, so the \$N(y)\approx13.5239\$. However, since September 13 in 1996 was a Friday, by Rule \$(1)\$, we must postpone by \$P(y)=1\$ day, so Rosh Hashanah falls on Saturday, September 14.

Let \$L(y)\$ be the number of days between September \$R(y)\$ in the year \$y\$ and September \$R(y+1)\$ in year \$y+1\$.

The first evening of Hannukah is:

$$ H(y)=\begin{cases} 83\text{ days after }R(y) & \text{if } L(y)\in\{355,385\}\\ 82\text{ days after }R(y) & \text{otherwise} \end{cases} $$

Notes and thanks

Your code may not access the Internet to get the answer using e.g. an online Hebrew calendar calculator.

Thank you to @Adám for pointing me to the rules and for a great deal of help. To keep things simple, this challenge assumes the location to be Jerusalem.

For those now intrigued by the math of the Hebrew calendar, see https://docs.microsoft.com/en-us/dotnet/api/system.globalization.hebrewcalendar?view=netframework-4.8 .

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Why the downvote? \$\endgroup\$
    – user9207
    Jan 5, 2020 at 20:04
  • 1
    \$\begingroup\$ I don't know Hannukah, just curious. I see the number 19 and 12 in the formula. Is that means it has some relationship with moon phase? \$\endgroup\$
    – tsh
    Jan 6, 2020 at 8:54
  • 1
    \$\begingroup\$ @tsh Yes. See e.g. en.m.wikipedia.org/wiki/Hebrew_calendar \$\endgroup\$
    – user9207
    Jan 6, 2020 at 9:29
  • \$\begingroup\$ Are we allowed to return a different date format depending on the year, or do the results have to be consistent? \$\endgroup\$
    – Adám
    Jan 8, 2020 at 13:48
  • \$\begingroup\$ @Adam That's fine. It just has to be easily human readable. \$\endgroup\$
    – user9207
    Jan 8, 2020 at 13:50

4 Answers 4

8
\$\begingroup\$

JavaScript (ES6),  188 ... 168  160 bytes

Assumes that the system is set up to UTC time (as the TIO servers are)

Returns a Date instance.

y=>(D=d=>new Date(y,8,n+=d))(((g=y=>D((n=80,d=D((3*-~(y/100)>>2)+1.554*(x=12*-~y%19)+y%4/4-y/314.6-.9).getDay())%2+2*(n%!d>.63&x>6))/864e5)(++y)-g(--y)+5)%30<1)

Try it online!

Or test it against a more literal implementation

How?

The approximations described below are guaranteed to work for \$665\le y\le 5510\$, which is a significantly larger interval than the requested one.

  • Instead of computing:

    $$k=\left\lfloor\frac{y}{100}\right\rfloor-\left\lfloor\frac{y}{400}\right\rfloor-2$$

    we compute \$k+2\$ with:

    3 * -~(y / 100) >> 2
    

    This off-by-two error is left uncorrected. We just need to adjust the final offset accordingly, as well as the tests about the day of the week. It has no impact on \$L(y)\$.

  • An offset of \$80\$ days is added right away to all date calculations, which introduces an additional shift of \$3\$ more days (\$80\bmod7\$) in the days of the week.

    Therefore, the index \$d\$ for the day of the week is \$5\$ for Sunday, \$6\$ for Monday, \$0\$ for Tuesday, \$1\$ for Wednesday, \$2\$ for Thursday, \$3\$ for Friday, or \$4\$ for Saturday.

    Very conveniently, it leads to the following properties:

    • rule \$(1)\$ for \$P(y)\$: the day is either Sunday, Wednesday or Friday iff \$d\$ is odd
    • rule \$(3)\$ for \$P(y)\$: the day is Tuesday iff \$d=0\$
  • We use \$1.554\$ as an approximation of \$\dfrac{765433}{492480}\$.

  • We use \$\dfrac{y}{314.6}+0.9\$ as an approximation of \$\dfrac{313y+89081}{98496}\$.

  • The test about \$D_y(\lfloor N(y)\rfloor)\$ being equal to \$1\$ is ignored entirely, as the result cancels out with the other criteria.

  • We use \$0.63\$ as an approximation of \$\dfrac{1367}{2160}\$.

  • Instead of testing \$L(y)\in\{355,385\}\$, we test \$\big(L(Y)+5\big)\bmod30=0\$.

\$\endgroup\$
3
  • \$\begingroup\$ Definitely tweetable :) Thank you for having the first answer. \$\endgroup\$
    – user9207
    Jan 4, 2020 at 21:56
  • \$\begingroup\$ I really like your description. Who knows, it might become the new way to calculate the date! \$\endgroup\$
    – user9207
    Jan 5, 2020 at 17:47
  • 1
    \$\begingroup\$ @Anush It still doesn't fit within the historical limit of 140 characters, but it's getting closer. ;) \$\endgroup\$
    – Arnauld
    Jan 6, 2020 at 17:02
3
\$\begingroup\$

C# (Visual C# Interactive Compiler), 108 bytes

An alternate version suggested by @Adám, with a distinct output format for the edge case 2239.

using System.Globalization;string f(int y)=>y<2239?new DateTime(y+3761,3,24,new HebrewCalendar())+"":"1221";

Try it online!


C# (Visual C# Interactive Compiler),  134 130 122  121 bytes

Saved 8 bytes thanks to @Hedi
Saved 1 byte thanks to @Adám

My first C# program either... \o/

Returns a DateTime instance.

using System.Globalization;dynamic f(int y)=>y<2239?new DateTime(y+3761,3,24,new HebrewCalendar()):new DateTime(y,12,21);

Try it online!

\$\endgroup\$
9
  • \$\begingroup\$ Tweetability at 140 characters! (Could you add the two remaining test cases too please.) \$\endgroup\$
    – user9207
    Jan 7, 2020 at 9:31
  • \$\begingroup\$ @Anush Oops... Seems like this only works up to \$y=2238\$. I'll probably have to add a patch for the last year. \$\endgroup\$
    – Arnauld
    Jan 7, 2020 at 16:02
  • 1
    \$\begingroup\$ @Anush Patch and test cases added. \$\endgroup\$
    – Arnauld
    Jan 7, 2020 at 16:22
  • \$\begingroup\$ @Arnauld are you missing the use of c in the case 2239 in new DateTime(y,12,21) ? If not you can save some bytes like this : using System.Globalization;DateTime f(int y)=>y<2239?new DateTime(y+3761,3,24,new HebrewCalendar()):new DateTime(y,12,21); \$\endgroup\$
    – Hedi
    Jan 7, 2020 at 20:50
  • \$\begingroup\$ Does this mean C# has a built-in for the Hebrew Calendar?! \$\endgroup\$
    – user9207
    Jan 7, 2020 at 21:11
2
\$\begingroup\$

Perl 5, 313 278 bytes

sub f{sub t{timegm_nocheck(0,0,0,pop,8,pop)}$Y=pop;eval'(R,N,Q)=map{X=12*(_%19+1)%19;N=1.554*X-2.9-_/314.4+_%4/4+_/100%99-_/400%9;I=intN;D=t(_,I)/86400%7;M=N-I;P=D=~/[361]/||D==4&&M>1?1:D==5&&M>=.63&&X>6?2:0;t(_,I+P),I+P}Y,Y+1;t(Y,N+82+((Q-R)/432e3)=~/1|7$/)'=~s/[A-Z_]/\$$&/gr}

Try it online!

Based on @Arnauld's javascript answer.

Slightly ungolfed:

sub f{
  sub t{timegm_nocheck(0,0,0,pop,8,pop)}            #xth sep y => secs after jan 1 1970
  $Y=pop;                                           #input year
  eval'                                             #eval code string to use code without $
    (R,N,Q) = map {                                 #R,N=rosh for curr Y. Q for Y+1
      X=12*(_%19+1)%19;
      N=1.554*X-2.9-_/314.4+_%4/4+_/100%99-_/400%9; #%99 cuts decimals, int() for small nums
      I=intN;
      D=t(_,I)/86400%7;                             #D=weekday - 4, 3=sun 6=wed 8=1=fri ...
      M=N-I;                                        #M=N%1 (perl dont support
      P=D=~/[361]/||D==4&&M>1?1:D==5&&M>=.63&&X>6?2:0;
      t(_,I+P),I+P
    }
    Y, Y+1;
    t(Y,N+82+((Q-R)/432e3)=~/1|7$/)
  ' =~ s/[A-Z_]/\$$&/gr                             #inserts $ before every uppercase A-Z and _
}
\$\endgroup\$
2
\$\begingroup\$

APL (Dyalog Unicode), 86 85 bytesSBCS

Now that Arnauld has posted his C# solution, I can reveal my equivalent anonymous tacit prefix function.

⎕NEW{⎕USING←'System'⋄0::1221⋄⍺⍺DateTime((3761+⍵)3 24,⍺⍺Globalization.HebrewCalendar)}

Can't try it online as this only works under Windows. (.NET Core support is planned for this year!)

⎕NEW{} function derived from monadic operator with ⎕NEW (create new instance of object) as operand:

⎕USING←'System' make the members of .NET's root namespace's immediately available

⋄0::1221 upon any error, return this number

Try:

  ⍺⍺DateTime() create a new DateTime object with the following constructor arguments:

   ⍺⍺Globalization.HebrewCalendar create a new HebrewCalendar object

   ()3 4, prepend the list ending with [,3,4] where the first element is:

    3761+⍵ the argument adjusted for the difference between the civil and Hebrew calendars' years

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