PHP Conference Japan 2024

easter_date

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

easter_date获取指定年份复活节的本地午夜的 Unix 时间戳

描述

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

返回对应于给定年份复活节午夜的 Unix 时间戳。

复活节的日期由公元 325 年尼西亚公会议确定为第一个满月后的第一个星期日,该满月在春分或之后出现。春分假定总是出现在 3 月 21 日,因此计算简化为确定满月的日期和随后星期日的日期。此处使用的算法由狄奥尼修斯·埃希格斯大约在 532 年提出。在儒略历(1753 年之前的年份)中,使用一个简单的 19 年周期来跟踪月球的相位。在格里高利历(1753 年之后的年份——由克拉维乌斯和利利乌斯设计,并于 1582 年 10 月由教皇格雷戈里十三世引入,并于 1752 年 9 月引入英国及其当时的殖民地)中,添加了两个校正因子以使周期更准确。

参数

year

年份必须是 32 位系统上的 1970 年到 2037 年之间的数字,或者 64 位系统上的 1970 年到 20 亿之间的数字。如果省略或为 null,则默认为根据本地时间计算的当前年份。

mode

当设置为 CAL_EASTER_ALWAYS_JULIAN 时,允许根据儒略历计算复活节日期。另请参阅 日历常量

返回值

复活节日期作为 Unix 时间戳。

错误/异常

如果年份早于 1970 年或晚于 32 位系统上的 2037 年,或者晚于 64 位系统上的 20 亿,则会抛出 ValueError 异常。

变更日志

版本 描述
8.3.0 在 64 位系统上,year 参数现在接受 1970 年到 20 亿之间的值。
8.0.0 year 现在可以为空。
8.0.0 year 超出允许范围时,现在会抛出 ValueError 异常。之前,会引发 E_WARNING 并返回 false

示例

示例 #1 easter_date() 示例

<?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

?>

示例 #2 使用 easter_date()DateTime

<?php

$timestamp
= easter_date(2023);

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

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

?>

注释

注意:

easter_date() 依赖于您系统的 C 库时间函数,而不是使用 PHP 的内部日期和时间函数。因此,easter_date() 使用 TZ 环境变量来确定它应该在其上运行的时区,而不是使用 PHP 的 默认时区,这可能导致在将此函数与 PHP 中的其他日期函数结合使用时出现意外行为。

作为解决方法,您可以使用 easter_days()DateTimeDateInterval 结合使用,以在您的 PHP 时区中计算复活节的开始日期,如下所示

<?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'));
}
?>

以上示例将输出

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

参见

  • easter_days() - 获取指定年份复活节在 3 月 21 日后的天数 用于计算 1970 年之前或 2037 年之后的复活节

添加注释

用户贡献的注释 7 条注释

maxie
16 年前
为了计算东正教教会的正确复活节日期,我基于 Meeus 儒略算法创建了一个函数

<?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;
}
?>
亚历山大·布利兹纽克
7年前
谢谢@Maxie,感谢你提供的计算东正教复活节日期的算法。
不过它可以改进。你加了13天来将儒略历映射到公历。但13天不是一个常数。它是格里高利历修正的累积误差,应该用这个公式计算:(int)($year / 100) - (int)($year / 400) - 2
[email protected]
14年前
我最近需要编写一个函数,让我知道今天是否是节假日。

在法国,我们有一些取决于复活节日期的节假日。也许这对某些人会有帮助。

只需在$holidays数组中修改你所在国家的实际节假日日期。

<?php
/**
* 此函数返回一个包含对应法国节假日的timestamp的数组
*/
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(
// 这些日期是固定的
mktime(0, 0, 0, 1, 1, $year), // 1月1日
mktime(0, 0, 0, 5, 1, $year), // 劳动节
mktime(0, 0, 0, 5, 8, $year), // 盟军胜利纪念日
mktime(0, 0, 0, 7, 14, $year), // 国庆节
mktime(0, 0, 0, 8, 15, $year), // 圣母升天节
mktime(0, 0, 0, 11, 1, $year), // 万圣节
mktime(0, 0, 0, 11, 11, $year), // 停战纪念日
mktime(0, 0, 0, 12, 25, $year), // 圣诞节

// 这些日期取决于复活节
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;
}
?>
纪尧姆·迪弗伦
11年前
我发现节假日时间戳计算和夏令时存在问题。
一篇关于此的文章在http://goo.gl/76t31(仅法语,抱歉)。

总之,今年(2013年)复活节开始前,需要增加一个小时的夏令时(发生在星期日凌晨3点)。这意味着,如果你执行$easter + X,其中x是等效于一天、39天或50天的秒数,结果并不等于午夜时间戳...

以下是一个函数,用于检查午夜时间戳是否等于节假日

function isHoliday( $ts ) {
// 许可证:知识共享署名(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;
}

欢迎使用/修改。
[email protected]
23小时前

<?php

class DateUtils {
/**
* 确定给定日期是否为法国(欧洲部分,不包括阿尔萨斯-摩泽尔)的法定节假日。
*
* @param DateTime $date 要检查的日期。
* @return bool 如果日期是法定节假日,则返回 True,否则返回 False。
*/
public static function isFrenchHoliday(DateTime $date): bool {

// 将要检查的日期转换为 Europe/Paris 时区 // 注意:这可能会影响与 UTC 时间相比的天数甚至年份
$dateToCheck = clone $date;
$dateToCheck->setTimezone(new DateTimeZone('Europe/Paris'));

$year = $dateToCheck->format('Y');
$easterDate = new DateTime('@'.easter_date($year), new DateTimeZone('UTC')); // 注意:如果没有 DateTimeZone('UTC'),从时间戳创建的日期仍然在 UTC 中,但明确指定它是一个好习惯。
/*
easter_date() 返回复活节的时间戳,该时间戳可转换为当地时间的 0:00,但基于 UTC 日期/时间!
例如,对于 2024-04-01,使用时间戳创建的 DateTime 将在 UTC 中为 2024-03-31 23:00,以便在系统中获得 UTC+1 以获得正确的结果(在 0:00)在当地时间(UTC+1)
*/
$easterDate->setTimezone(new DateTimeZone('Europe/Paris'));

$holidays = [
// 固定节假日
'01-01', // 元旦
'01-05', // 劳动节
'08-05', // 欧洲胜利日
'14-07', // 巴士底日
'15-08', // 圣母升天节
'01-11', // 万圣节
'11-11', // 停战纪念日
'25-12', // 圣诞节

// 可变节假日
// 注意:复活节(星期日)未列为法国法定节假日
(clone $easterDate)->modify('+1 day')->format('d-m'), // 复活节星期一(复活节 + 1 天)
(clone $easterDate)->add(new DateInterval('P39D'))->format('d-m'), // 耶稣升天节(复活节 + 39 天)
// 五旬节在复活节后的第七个星期日庆祝,即 7x7 天,或 49 天...
// 注意:五旬节未列为法国法定节假日
(clone $easterDate)->add(new DateInterval('P50D'))->format('d-m'), // 圣灵降临节星期一(复活节 + 50 天)
];
// 注意:阿尔萨斯-摩泽尔有两个额外的法定节假日。
// 注意:在瓜德罗普、圭亚那、马提尼克、马约特、留尼汪、圣巴泰勒米和圣马丁,废除奴隶制被纪念为法国法定节假日 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()
{
// 数据来源: https://www.service-public.fr/particuliers/actualites/A15406
return [
[
'2024-01-01'], // 元旦
['2024-04-01'], // 复活节星期一
['2024-05-01'], // 劳动节
['2024-05-08'], // 欧洲胜利日
['2024-05-09'], // 耶稣升天节
['2024-05-20'], // 圣灵降临节星期一
['2024-07-14'], // 巴士底日
['2024-08-15'], // 圣母升天节
['2024-11-01'], // 万圣节
['2024-11-11'], // 停战纪念日
['2024-12-25'], // 圣诞节
];
}

// public function testIsFrenchHolidayHandlesDatesAroundMidnight($dateString, $fromTimeZone, $toTimeZone, $expected)
// ...
}
?>
phpuser
20 年前
如果添加一些 (int) 强制类型转换,Bigtree 的算法是正确的。
<?php
function easter_date ($Year) {

/*
G 为复活节黄金数减 1
H 为 23 减去朔日 (模 30)
I 为从 3 月 21 日到复活节满月的日期数
J 为复活节满月的星期几 (0=星期日,
1=星期一,等等)
L 为从 3 月 21 日到复活节满月或之前星期日的日期数 (介于 -6 和 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;

}
?>
adwil at live dot com
10 年前
嗨,最近我需要一个函数来获取在线商店的实现日期,所以这里有(已准备好用于波兰用户,请根据其他国家/地区调整您的日期)

<?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'); //静态节假日日期数组(这些来自波兰)
$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); //复活节
$easterSec = date('m-d', strtotime('+1 day', $date)); //复活节星期一
$greens = date('m-d', strtotime('+49 days', $date)); //五旬节
$cc = date('m-d', strtotime('+60 days', $date)); //圣体节
$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