15
\$\begingroup\$

Your function or program should take a year as input and return (or print) the date (in the Gregorian calendar) of that years Easter (not the Eastern Orthodox Easter). The date returned should be formatted according to ISO 8601, but with support for years greater than 9999 (such as 312013-04-05 or 20010130), and it only needs to work with years greater than or equal to 1583 (the year of the adoption of the Gregorian calendar), and years less than or equal to 5701583 (as that is when the sequence of Easter dates starts to repeat itself).

Examples:

e(5701583) = 5701583-04-10
e(2013)    = 2013-03-31
e(1583)    = 1583-04-10
e(3029)    = 30290322
e(1789)    = 17890412
e(1725)    = 17250401

The use of built-in functions for returning the date of easter is boring and therefore disallowed. Shortest answer (in characters) wins.

Resources:

\$\endgroup\$
13
  • \$\begingroup\$ Do you realise that some languages have a built-in function to do this? \$\endgroup\$ Apr 5, 2013 at 13:58
  • \$\begingroup\$ Such as? The only one I am aware of is PHP, but the easter_date and easter_days functions are quite limited, easter_date only works for years after 1970 and easter_days doesn't return the correct amount of days for years prior to 1753. But I will edit the question to disallow the use of such functions. \$\endgroup\$
    – Fors
    Apr 5, 2013 at 14:06
  • 1
    \$\begingroup\$ resource: en.wikipedia.org/wiki/Computus#Algorithms \$\endgroup\$ Apr 5, 2013 at 14:32
  • 1
    \$\begingroup\$ So this is Gregorian and NOT Julian? Also, I'm not Catholic, what is the "Catholic Tradition?" \$\endgroup\$ Apr 5, 2013 at 16:20
  • 2
    \$\begingroup\$ More functions \$\endgroup\$
    – SeanC
    Apr 9, 2013 at 13:41

9 Answers 9

5
\$\begingroup\$

GolfScript (85 chars)

~:^100/.)3*4/.@8*13+25/-^19%.19*15+@+30%.@11/+29/23--.@-^.4/++7%97--^[email protected]/100*\31%)+

Sample usage:

$ golfscript.rb codegolf11132.gs <<<2013
20130331

Note that this uses a different algorithm to most of the current answers. To be specific, I've adapted the algorithm attributed to Lichtenberg in the resource linked by Sean Cheshire in a comment on the question.

The original algorithm, assuming sensible types (i.e. not JavaScript's numbers) and with an adaptation to give month*31+day (using day offset of 0) is

K = Y/100
M = 15 + (3*K+3)/4 - (8*K+13)/25
S = 2 - (3*K+3)/4
A = Y%19
D = (19*A+M) % 30
R = (D + A/11)/29
OG = 21 + D - R
SZ = 7 - (Y + Y/4 + S) % 7
OE = 7 - (OG-SZ) % 7
return OG + OE + 92

I extracted a common subexpression and did some other optimisations to reduce to

K = y/100
k = (3*K+3)/4
A = y%19
D = (19*A+15+k-(8*K+13)/25)%30
G = 23+D-(D+A/11)/29
return 97+G-(G+y+y/4-k)%7

This approach has slightly more arithmetic operations than the other one (Al Petrofsky's 20-op algorithm), but it has smaller constants; GolfScript doesn't need to worry about the extra parentheses because it's stack-based, and since each intermediate value in my optimised layout is used precisely twice it fits nicely with GolfScript's limitation of easy access to the top three items on the stack.

\$\endgroup\$
2
  • \$\begingroup\$ It has a small issue, when the date of Easter lies between the 1st of april and the 10th of april, it returns dates such as 1725041, when it should return 17250401. But upvoted for the different approach! \$\endgroup\$
    – Fors
    Apr 11, 2013 at 5:32
  • \$\begingroup\$ @Fors, oops. Now fixed. \$\endgroup\$ Apr 11, 2013 at 7:05
5
\$\begingroup\$

Python 2 - 125 120 119 chars

This is Fors's answer shamelessly ported to Python.

y=input()
a=y/100*1483-y/400*2225+2613
b=(y%19*3510+a/25*319)/330%29
b=148-b-(y*5/4+a-b)%7
print(y*100+b/31)*100+b%31+1

Edit: Changed last line from print"%d-0%d-%02d"%(y,b/31,b%31+1) to save 5 characters. I would have loved to represent 10000 as 1e4, but that would produce floating point necessitating a call to int.

Edit2: Thanks to Peter Taylor for showing how to get rid of that 10000 and save 1 character.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ If you split 10000up to 100*100 you can put the last line into Horner's form as (y*100+b/31)*100+b%31+1. The leading parenthesis allows you to remove the space after print, and you can pull out the three instances of 100 to a variable for an overall saving of 1 char. \$\endgroup\$ Apr 9, 2013 at 14:34
  • \$\begingroup\$ @PeterTaylor: Excellent suggestion. Updated my answer. \$\endgroup\$ Apr 9, 2013 at 14:50
  • \$\begingroup\$ You can make it a function e(y) and save a few bytes \$\endgroup\$
    – sagiksp
    Feb 2, 2017 at 8:28
4
\$\begingroup\$

PHP 154

150 chars if I switch to YYYYMMDD instead of YYYY-MM-DD.

<?$y=$argv[1];$a=$y/100|0;$b=$a>>2;$c=($y%19*351-~($b+$a*29.32+13.54)*31.9)/33%29|0;$d=56-$c-~($a-$b+$c-24-$y/.8)%7;echo$d>31?"$y-04-".($d-31):"$y-03-$d";

With Line Breaks:

<?
$y = $argv[1];
$a = $y / 100 |0;
$b = $a >> 2;
$c = ($y % 19 * 351 - ~($b + $a * 29.32 + 13.54) * 31.9) / 33 % 29 |0;
$d = 56 - $c - ~($a - $b + $c - 24 - $y / .8) % 7;
echo $d > 31 ? "$y-04-".($d - 31) : "$y-03-$d";

Useage: php easter.php 1997
Output: 1997-03-30

Useage: php easter.php 2001
Output: 2001-04-15

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Great algorithm golfing, not so great code golfing. I took the freedom to chop off 18 bytes: <?=$y=$argv[1],"-0",3+$m=($d=56-($c=($y%19*351-~(($a=$y/100|0)*29.32+($b=$a>>2)+13.54)*31.9)/33%29)-~($a-$b+$c-24-$y/.8)%7)>>5,31*$m-$d; \$\endgroup\$
    – Titus
    Feb 2, 2017 at 6:13
  • \$\begingroup\$ Fails to meet output format. The leading zero for the day is missing where needed. E.g. for year 1725 it outputs 1725-04-1 instead of 1725-04-01. \$\endgroup\$
    – Christoph
    Feb 2, 2017 at 10:47
4
\$\begingroup\$

dc: 106 characters

?[0n]smdndsy100/1483*ly400/2225*-2613+dsa25/319*ly19%3510*+330/29%sb148lb-5ly*4/la+lb-7%-d31/0nn31%1+d9>mp

Usage:

> dc -e "?[0n]smdndsy100/1483*ly400/2225*-2613+dsa25/319*ly19%3510*+330/29%sb148lb-5ly*4/la+lb-7%-d31/0nn31%1+d9>mp"
1725
17250401
>

This should be able to be shortened by using 'd' and 'r' instead of all the loads and stores.

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

C: 151 148 characters

y;a;b;main(){scanf("%d",&y);a=y/100*1483-y/400*2225+2613;b=(y%19*3510+a/25*319)/330%29;b=148-b-(y*5/4+a-b)%7;printf("%d-0%d-%02d\n",y,b/31,b%31+1);}

And the same code, but better formatted:

#include <stdio.h>

int y, a, b;

int main() {
    scanf("%d", &y);

    a = y/100*1483 - y/400*2225 + 2613;
    b = (y%19*3510 + a/25*319)/330%29;
    b = 148 - b - (y*5/4 + a - b)%7;

    printf("%d-0%d-%02d\n", y, b/31, b%31 + 1);
}

There are frightfully many algorithms for calculating the date of Easter, but only a few of them are well suited for code golfing.

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

Javascript 162 156 145

function e(y){alert(y+"0"+((d=56-(c=(y%19*351-~((b=(a=y/100|0)>>2)+a*29.32+13.54)*31.9)/33%29|0)-~(a-b+c-24-y/.8)%7)>(f=31)?4:3)+(d-f>0&d-f<10?0:"")+(d>f?d-f:d))}

Inspired by @jdstankosky's PHP solution... Provides YYYYMMDD result...

Now narrowed down to:

alert((y=prompt())+0+((d=56-(c=(y%19*351-~((b=(a=y/100|0)>>2)+a*29.32+13.54)*31.9)/33%29|0)-~(a-b+c-24-y/.8)%7)>(f=31)?4:3)+(d-f>0&d-f<10?0:"")+(d>f?d-f:d))

Now asks for input... reduced literal string of "0" to 0 and let loose typing work to my advantage! :)

Reduced further to take ES6 into account...

e=y=>y+"0"+((d=56-(c=(y%19*351-31.9*~((b=(a=y/100|0)>>2)+29.32*a+13.54))/33%29|0)-~(a-b+c-24-y/.8)%7)>(f=31)?4:3)+(d-f>0&d-f<10?0:"")+(d>f?d-f:d)

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

APL 132

This algorithm calculates the number of days Easter lies relative to the beginning of March. The date is returned in the YYYYMMDD format as allowed in the question:

E y                                                   
(a b)←⌊((3 8×⌊y÷100)+¯5 13)÷4 25                           
c←7|y+(⌊y÷4)-a-e←⌊d-((19×d←30|(227-(11×c)-a-b))+c←19|y)÷543
+/(10*4 2 0)×y,(3+i>31),(61⍴⍳31)[i←e+28-c] 

Taking the original test cases:

      E 2013
20130331
      E 1583
15830410
      E 3029
30290322
      E 1789
17890412         
\$\endgroup\$
1
\$\begingroup\$

Javascript 134 Characters

Using a slightly modified Al Petrofsky's 20-op algorithm to allow a simpler conversion into the year-month-date format.

This provides additional 14 characters shorter code from the answer provided by WallyWest

The Original Al Petrofsky's Code:

    a =Y/100|0,
    b =a>>2,
    c =(Y%19*351-~(b+a*29.32+13.54)*31.9)/33%29|0,
    d =56-c-~(a-b+c-24-Y/.8)%7;

Last line changed to:

d =148-c-~(a-b+c-24-Y/.8)%7;

1. 138 Characters With the format YYYY-MM-DD

e=y=>(c=(y%19*351-31.9*~((b=(a=y/100|0)>>2)+29.32*a+13.54))/33%29|0,d=148-c-~(a-b+c-24-y/.8)%7,y+'-0'+(d/31|0)+"-"+((o=d%31+1)>9?o:'0'+o))

console.log(e(2021));
console.log(e(2022));
console.log(e(5701583));
console.log(e(2013));
console.log(e(1583));
console.log(e(3029));
console.log(e(1789));
console.log(e(1725));

2. 134 Characters With the format YYYYMMDD

e=y=>(c=(y%19*351-31.9*~((b=(a=y/100|0)>>2)+29.32*a+13.54))/33%29|0,d=148-c-~(a-b+c-24-y/.8)%7,y+'0'+(d/31|0)+((d=d%31+1)>9?d:'0'+d))

console.log(e(2021));
console.log(e(2022));
console.log(e(5701583));
console.log(e(2013));
console.log(e(1583));
console.log(e(3029));
console.log(e(1789));
console.log(e(1725));

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

Fortran (GFortran), 179 bytes

READ*,I
J=I/100*2967-I/400*8875+7961
K=MOD(MOD(I,19)*6060+(MOD(MOD(J/25,59),30)+23)*319-1,9570)/330
L=K+28-MOD(I*5/4+J+K,7)
WRITE(*,'(I7,I0.2,I0.2)')I,(L-1)/31+3,MOD(L-1,31)+1
END

Try it online!

Uses the "Emended Gregorian Easter" algorithm (Al Petrofsky) from the second resource link. Strangely, it fails for year 5701583 (and, apparently, only for this year), predicting the Easter as one week earlier. Prints the date in YYYYYYYMMDD format, with some leading spaces if the year has less then seven digits.

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