ConFoo 2025

easter_date

(PHP 4, PHP 5, PHP 7, PHP 8)

easter_dateGet Unix timestamp for local midnight on Easter of a given year

Description

easter_date(?int $year = null, int $mode = CAL_EASTER_DEFAULT): int

Returns the Unix timestamp corresponding to midnight on Easter of the given year.

The date of Easter Day was defined by the Council of Nicaea in AD325 as the Sunday after the first full moon which falls on or after the Spring Equinox. The Equinox is assumed to always fall on 21st March, so the calculation reduces to determining the date of the full moon and the date of the following Sunday. The algorithm used here was introduced around the year 532 by Dionysius Exiguus. Under the Julian Calendar (for years before 1753) a simple 19-year cycle is used to track the phases of the Moon. Under the Gregorian Calendar (for years after 1753 - devised by Clavius and Lilius, and introduced by Pope Gregory XIII in October 1582, and into Britain and its then colonies in September 1752) two correction factors are added to make the cycle more accurate.

Parameters

year

The year must be a number between 1970 and 2037 for 32-bit systems, or between 1970 and 2,000,000,000 for 64-bit systems. If omitted or null, defaults to the current year according to the local time.

mode

Allows Easter dates to be calculated based on the Julian calendar when set to CAL_EASTER_ALWAYS_JULIAN. See also calendar constants.

Return Values

The easter date as a unix timestamp.

Errors/Exceptions

A ValueError is thrown if the year is earlier than 1970 or later than 2037 when running on a 32-bit system, or later than 2,000,000,000 on a 64-bit system.

Changelog

Version Description
8.3.0 On 64-bit systems, the year parameter now accepts values within the range of 1970 to 2,000,000,000.
8.0.0 year is nullable now.
8.0.0 A ValueError is now thrown when year is outside the allowed range. Previously, an E_WARNING was raised and the function returned false.

Examples

Example #1 easter_date() example

<?php

echo date("M-d-Y", easter_date(1999)); // Apr-04-1999
echo date("M-d-Y", easter_date(2000)); // Apr-23-2000
echo date("M-d-Y", easter_date(2001)); // Apr-15-2001

?>

Example #2 Using easter_date() with DateTime

<?php

$timestamp
= easter_date(2023);

$datetime = new \DateTime();
$datetime->setTimestamp($timestamp);

echo
$datetime->format('M-d-Y'); // Apr-09-2023

?>

Notes

Note:

easter_date() relies on your system's C library time functions, rather than using PHP's internal date and time functions. As a consequence, easter_date() uses the TZ environment variable to determine the time zone it should operate in, rather than using PHP's default time zone, which may result in unexpected behaviour when using this function in conjunction with other date functions in PHP.

As a workaround, you can use the easter_days() with DateTime and DateInterval to calculate the start of Easter in your PHP time zone as follows:

<?php
function get_easter_datetime($year) {
$base = new DateTime("$year-03-21");
$days = easter_days($year);

return
$base->add(new DateInterval("P{$days}D"));
}

foreach (
range(2012, 2015) as $year) {
printf("Easter in %d is on %s\n",
$year,
get_easter_datetime($year)->format('F j'));
}
?>

The above example will output:

Easter in 2012 is on April 8
Easter in 2013 is on March 31
Easter in 2014 is on April 20
Easter in 2015 is on April 5

See Also

  • easter_days() - Get number of days after March 21 on which Easter falls for a given year for calculating Easter before 1970 or after 2037

add a note

User Contributed Notes 7 notes

up
30
maxie
16 years ago
To compute the correct Easter date for Eastern Orthodox Churches I made a function based on the Meeus Julian algorithm:

<?php
function orthodox_eastern($year) {
$a = $year % 4;
$b = $year % 7;
$c = $year % 19;
$d = (19 * $c + 15) % 30;
$e = (2 * $a + 4 * $b - $d + 34) % 7;
$month = floor(($d + $e + 114) / 31);
$day = (($d + $e + 114) % 31) + 1;

$de = mktime(0, 0, 0, $month, $day + 13, $year);

return
$de;
}
?>
up
9
Alexander Bliznyuk
7 years ago
Thank you, @Maxie, for algorythm for computing Orthodox Easter date.
It can be improved though. You added 13 days in order to map Julian calendar to Gregorian. But 13 days is not a constant. It's an accumulated error fixed in Gregorian and should be calculated with this formula: (int)($year / 100) - (int)($year / 400) - 2
up
11
py dot lebecq at gmail dot com
14 years ago
I recently had to write a function that allows me to know if today is a holiday.

And in France, we have some holidays which depends on the easter date. Maybe this will be helpful to someone.

Just modify in the $holidays array the actual holidays dates of your country.

<?php
/**
* This function returns an array of timestamp corresponding to french holidays
*/
protected static function getHolidays($year = null)
{
if (
$year === null)
{
$year = intval(date('Y'));
}

$easterDate = easter_date($year);
$easterDay = date('j', $easterDate);
$easterMonth = date('n', $easterDate);
$easterYear = date('Y', $easterDate);

$holidays = array(
// These days have a fixed date
mktime(0, 0, 0, 1, 1, $year), // 1er janvier
mktime(0, 0, 0, 5, 1, $year), // Fête du travail
mktime(0, 0, 0, 5, 8, $year), // Victoire des alliés
mktime(0, 0, 0, 7, 14, $year), // Fête nationale
mktime(0, 0, 0, 8, 15, $year), // Assomption
mktime(0, 0, 0, 11, 1, $year), // Toussaint
mktime(0, 0, 0, 11, 11, $year), // Armistice
mktime(0, 0, 0, 12, 25, $year), // Noel

// These days have a date depending on easter
mktime(0, 0, 0, $easterMonth, $easterDay + 2, $easterYear),
mktime(0, 0, 0, $easterMonth, $easterDay + 40, $easterYear),
mktime(0, 0, 0, $easterMonth, $easterDay + 50, $easterYear),
);

sort($holidays);

return
$holidays;
}
?>
up
1
Guillaume Dufrene
11 years ago
I found a problem with holidays timestamp computation and daylight saving time.
An article about it at http://goo.gl/76t31 (in french only, sorry).

In summary, this year (2013) easter begins before adding an hour for daylight saving time (occured sunday at 3:00). It means that if you do $easter + X, where x is a number of seconds equivalent to one day, 39 days or 50 days, the result is not equals to a midnight timestamp...

Here a function to check if a midnight timestamp is equals to an holiday :

function isHoliday( $ts ) {
// Licence : Creative Commons (BY)
// By Webpulser - http://goo.gl/76t31
$fixed_holidays = array( ’01-01′, ’01-05′, ’08-05′, ’14-07′, ’15-08′, ’11-11′, ’25-12′ );
$format = ‘d-m’;

$dm = date($format, $ts);
if ( in_array($dm, $fixed_holidays) ) return true;

$easter = easter_date( date(‘Y’, $ts) );
if ( date($format, $easter + 86400) == $dm ) return true;
if ( date($format, $easter + 3369600) == $dm ) return true;
if ( date($format, $easter + 4320000) == $dm ) return true;

return false;
}

feel free to use / modify.
up
0
alexandre dot php dot net at exmackina dot com
3 days ago
<?php

class DateUtils {
/**
* Determines if a given date is a public holiday in european France, excluding Alsace-Moselle.
*
* @param DateTime $date The date to check.
* @return bool True if the date is a public holiday, False otherwise.
*/
public static function isFrenchHoliday(DateTime $date): bool {

// Convert the date to check to Europe/Paris time // Note: This can affect the day or even the year compared to UTC time
$dateToCheck = clone $date;
$dateToCheck->setTimezone(new DateTimeZone('Europe/Paris'));

$year = $dateToCheck->format('Y');
$easterDate = new DateTime('@'.easter_date($year), new DateTimeZone('UTC')); // Note: Without DateTimeZone('UTC'), the date created from the timestamp is still in UTC, but it's a good practice to specify it explicitly.
/*
easter_date() returns a timestamp for Easter that is convertible to 0:00 in local time, but based on a UTC date/time!
For example, for 2024-04-01, a DateTime created with the timestamp will be on 2024-03-31 23:00 UTC in a system with UTC+1 in order to get the correct result (at 0:00) in local time (UTC+1)
*/
$easterDate->setTimezone(new DateTimeZone('Europe/Paris'));

$holidays = [
// Fixed holidays
'01-01', // New Year's Day
'01-05', // Lab0r Day
'08-05', // Vict0ry in Europe Day
'14-07', // Bastille Day
'15-08', // Assumption of Mary
'01-11', // All Saints' Day
'11-11', // Armistice Day
'25-12', // Christmas Day

// Movable holidays
// Note: Easter (Sunday) is not listed as a public French holiday
(clone $easterDate)->modify('+1 day')->format('d-m'), // Easter Monday (Easter + 1 day)
(clone $easterDate)->add(new DateInterval('P39D'))->format('d-m'), // Ascension (Easter + 39 days)
// Pentecost is celebrated on the seventh Sunday after Easter, i.e., 7x7 days, or 49 days...
// Note: Pentecost is not listed as a public French holiday
(clone $easterDate)->add(new DateInterval('P50D'))->format('d-m'), // Whit Monday (Easter + 50 days)
];
// Note: Alsace-Moselle has two additional public holidays.
// Note: In Guadeloupe, Guyana, Martinique, Mayotte, Réunion, Saint-Barthélemy, and Saint-Martin, the abolition of slavery is commemorated as a public French holiday https://www.legifrance.gouv.fr/codes/section_lc/LEGITEXT000006072050/LEGISCTA000006160771/

return in_array($dateToCheck->format('d-m'), $joursFeries);
}
}
?>
<?php
class DateUtilsTest extends TestCase
{

public function
testIsFrenchHolidayReturnsFalseForNonHoliday()
{
$date = new DateTime('2023-09-27');
$this->assertFalse(DateUtils::isFrenchHoliday($date));
}

/**
* @dataProvider datesFor2024
*/
public function testIsFrenchHolidayReturnsTrueFor2024($dateString)
{
$date = new DateTime($dateString);
$this->assertTrue(DateUtils::isFrenchHoliday($date));
}

public function
datesFor2024()
{
// source: https://www.service-public.fr/particuliers/actualites/A15406
return [
[
'2024-01-01'], // New Year's Day
['2024-04-01'], // Easter Monday
['2024-05-01'], // Labor Day
['2024-05-08'], // Victory in Europe Day
['2024-05-09'], // Ascension
['2024-05-20'], // Whit Monday
['2024-07-14'], // Bastille Day
['2024-08-15'], // Assumption of Mary
['2024-11-01'], // All Saints' Day
['2024-11-11'], // Armistice Day
['2024-12-25'], // Christmas Day
];
}

// public function testIsFrenchHolidayHandlesDatesAroundMidnight($dateString, $fromTimeZone, $toTimeZone, $expected)
// ...
}
?>
up
-1
phpuser
20 years ago
The algorithm from Bigtree is correct if you add some (int) cast
<?php
function easter_date ($Year) {

/*
G is the Golden Number-1
H is 23-Epact (modulo 30)
I is the number of days from 21 March to the Paschal full moon
J is the weekday for the Paschal full moon (0=Sunday,
1=Monday, etc.)
L is the number of days from 21 March to the Sunday on or before
the Paschal full moon (a number between -6 and 28)
*/


$G = $Year % 19;
$C = (int)($Year / 100);
$H = (int)($C - (int)($C / 4) - (int)((8*$C+13) / 25) + 19*$G + 15) % 30;
$I = (int)$H - (int)($H / 28)*(1 - (int)($H / 28)*(int)(29 / ($H + 1))*((int)(21 - $G) / 11));
$J = ($Year + (int)($Year/4) + $I + 2 - $C + (int)($C/4)) % 7;
$L = $I - $J;
$m = 3 + (int)(($L + 40) / 44);
$d = $L + 28 - 31 * ((int)($m / 4));
$y = $Year;
$E = mktime(0,0,0, $m, $d, $y);

return
$E;

}
?>
up
-3
adwil at live dot com
10 years ago
Hey, recently I needed a function to get realization dates in online shop, so here it is (ready to go for polish users, please adjust your dates for any other country):

<?php
function getWorkday($date1,$workDays) {
$workDays = (int)$workDays;
if (
$workDays <= 0)
return
null;

$date1=strtotime('-1 day',strtotime($date1));

$lastYear = null;
$hol=array('01-01','01-06','05-01','05-03','08-15','11-01','11-11','12-25','12-26'); //array of month-date of static holidays (these are from Poland)
$i = 0;
while (
$i<=$workDays) {
$year = date('Y', $date1);
if (
$year !== $lastYear){
$lastYear = $year;
$easter = date('m-d', easter_date($year));
$date = strtotime($year . '-' . $easter); // easter
$easterSec = date('m-d', strtotime('+1 day', $date)); // easter monday
$greens = date('m-d', strtotime('+49 days', $date)); // zielone swiatki
$cc = date('m-d', strtotime('+60 days', $date)); // boze cialo
$hol[] = $easter;
$hol[] = $easterSec;
$hol[] = $greens;
$hol[] = $cc;
}
$weekDay=date('w',$date1);
if (!(
$weekDay==0 || $weekDay==6 || in_array(date('m-d',$date1),$hol)))
$i++;

$date1=strtotime('+1 day',$date1);
}
return
date('Y-m-d',$date1);
}
?>
To Top