Calculate the date of Easter

13

2

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:

Fors

Posted 2013-04-05T13:29:43.297

Reputation: 3 020

Do you realise that some languages have a built-in function to do this? – Peter Taylor – 2013-04-05T13:58:08.437

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. – Fors – 2013-04-05T14:06:49.913

R has a function Easter in package timeDate that works for any year of the gregorian calendar. – plannapus – 2013-04-05T14:09:33.803

1

resource: http://en.wikipedia.org/wiki/Computus#Algorithms

– John Dvorak – 2013-04-05T14:32:57.443

1So this is Gregorian and NOT Julian? Also, I'm not Catholic, what is the "Catholic Tradition?" – jdstankosky – 2013-04-05T16:20:11.233

What's the scoring method? Fewest characters? – Mr. Llama – 2013-04-05T17:16:48.890

@jdstankosky, I wrote "according to the Catholic tradition" just to imply that it is Gregorian, I could perhaps clarify it a little. – Fors – 2013-04-05T17:31:36.123

Peter Taylor is correct. The function in Mathematica's Calendar package is EasterSunday[year]. No, I won't be submitting it. – DavidC – 2013-04-05T21:43:42.563

2More functions – SeanC – 2013-04-09T13:41:32.800

Bearing in mind that ISO 8601 doesn't naturally support years greater than 9999, is the answer required to support them? – Peter Taylor – 2013-04-09T14:44:34.097

@PeterTaylor, a good question. But I do think that the answer at least needs to support years up to 5,701,583, as that is when the cycle of dates recur (and it is doable using signed 32-bit integers). I will update the question and add a couple of test cases. – Fors – 2013-04-11T05:45:57.567

txt file link is an empty file. – sergiol – 2017-01-09T10:52:38.650

@sergiol That's fixed now, I replaced the link with a version cached by archive.org, which should future proof it for quite a while. – Fors – 2017-01-11T15:33:09.163

Answers

3

GolfScript (85 chars)

~:^100/.)3*4/.@8*13+25/-^19%.19*15+@+30%.@11/+29/23--.@-^.4/++7%97--^0@.31/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.

Peter Taylor

Posted 2013-04-05T13:29:43.297

Reputation: 41 901

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! – Fors – 2013-04-11T05:32:06.003

@Fors, oops. Now fixed. – Peter Taylor – 2013-04-11T07:05:01.930

5

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.

Steven Rumbalski

Posted 2013-04-05T13:29:43.297

Reputation: 1 353

1If 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. – Peter Taylor – 2013-04-09T14:34:57.200

@PeterTaylor: Excellent suggestion. Updated my answer. – Steven Rumbalski – 2013-04-09T14:50:29.803

You can make it a function e(y) and save a few bytes – sagiksp – 2017-02-02T08:28:47.573

4

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

jdstankosky

Posted 2013-04-05T13:29:43.297

Reputation: 1 474

1Great 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; – Titus – 2017-02-02T06:13:36.277

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. – Christoph – 2017-02-02T10:47:36.123

4

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.

Fors

Posted 2013-04-05T13:29:43.297

Reputation: 3 020

3

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.

Fors

Posted 2013-04-05T13:29:43.297

Reputation: 3 020

3

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)

WallyWest

Posted 2013-04-05T13:29:43.297

Reputation: 6 949

2

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         

Graham

Posted 2013-04-05T13:29:43.297

Reputation: 3 184

0

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.

rafa11111

Posted 2013-04-05T13:29:43.297

Reputation: 310