1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Shane Caraveo <shane@caraveo.com> |
14 | Colin Viebrock <colin@easydns.com> |
15 | Hartmut Holzgraefe <hholzgra@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20 #include "php_calendar.h"
21 #include "sdncal.h"
22 #include <time.h>
23
_cal_easter(INTERNAL_FUNCTION_PARAMETERS,bool gm)24 static void _cal_easter(INTERNAL_FUNCTION_PARAMETERS, bool gm)
25 {
26 /* based on code by Simon Kershaw, <webmaster@ely.anglican.org> */
27
28 struct tm te;
29 zend_long year, golden, solar, lunar, pfm, dom, tmp, easter, result;
30 zend_long method = CAL_EASTER_DEFAULT;
31 const zend_long max_year = (zend_long)(ZEND_LONG_MAX / 5) * 4;
32 bool year_is_null = 1;
33
34 if (zend_parse_parameters(ZEND_NUM_ARGS(),
35 "|l!l", &year, &year_is_null, &method) == FAILURE) {
36 RETURN_THROWS();
37 }
38
39 /* Default to the current year if year parameter is not given */
40 if (year_is_null) {
41 time_t a;
42 struct tm b, *res;
43 time(&a);
44 res = php_localtime_r(&a, &b);
45 if (!res) {
46 year = 1900;
47 } else {
48 year = 1900 + b.tm_year;
49 }
50 }
51
52 if (year <= 0 || year > max_year) {
53 zend_argument_value_error(1, "must be between 1 and " ZEND_LONG_FMT, max_year);
54 RETURN_THROWS();
55 }
56
57 if (gm && (year<1970 || year>2037)) { /* out of range for timestamps */
58 zend_argument_value_error(1, "must be between 1970 and 2037 (inclusive)");
59 RETURN_THROWS();
60 }
61
62 golden = (year % 19) + 1; /* the Golden number */
63
64 if ((year <= 1582 && method != CAL_EASTER_ALWAYS_GREGORIAN) ||
65 (year >= 1583 && year <= 1752 && method != CAL_EASTER_ROMAN && method != CAL_EASTER_ALWAYS_GREGORIAN) ||
66 method == CAL_EASTER_ALWAYS_JULIAN) { /* JULIAN CALENDAR */
67
68 dom = (year + (year/4) + 5) % 7; /* the "Dominical number" - finding a Sunday */
69 if (dom < 0) {
70 dom += 7;
71 }
72
73 pfm = (3 - (11*golden) - 7) % 30; /* uncorrected date of the Paschal full moon */
74 if (pfm < 0) {
75 pfm += 30;
76 }
77 } else { /* GREGORIAN CALENDAR */
78 dom = (year + (year/4) - (year/100) + (year/400)) % 7; /* the "Domincal number" */
79 if (dom < 0) {
80 dom += 7;
81 }
82
83 solar = (year-1600)/100 - (year-1600)/400; /* the solar and lunar corrections */
84 lunar = (((year-1400) / 100) * 8) / 25;
85
86 pfm = (3 - (11*golden) + solar - lunar) % 30; /* uncorrected date of the Paschal full moon */
87 if (pfm < 0) {
88 pfm += 30;
89 }
90 }
91
92 if ((pfm == 29) || (pfm == 28 && golden > 11)) { /* corrected date of the Paschal full moon */
93 pfm--; /* - days after 21st March */
94 }
95
96 tmp = (4-pfm-dom) % 7;
97 if (tmp < 0) {
98 tmp += 7;
99 }
100
101 easter = pfm + tmp + 1; /* Easter as the number of days after 21st March */
102
103 if (gm) { /* return a timestamp */
104 te.tm_isdst = -1;
105 te.tm_year = year-1900;
106 te.tm_sec = 0;
107 te.tm_min = 0;
108 te.tm_hour = 0;
109
110 if (easter < 11) {
111 te.tm_mon = 2; /* March */
112 te.tm_mday = easter+21;
113 } else {
114 te.tm_mon = 3; /* April */
115 te.tm_mday = easter-10;
116 }
117 result = mktime(&te);
118 } else { /* return the days after March 21 */
119 result = easter;
120 }
121 ZVAL_LONG(return_value, result);
122 }
123
124 /* {{{ Return the timestamp of midnight on Easter of a given year (defaults to current year) */
PHP_FUNCTION(easter_date)125 PHP_FUNCTION(easter_date)
126 {
127 _cal_easter(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
128 }
129 /* }}} */
130
131 /* {{{ Return the number of days after March 21 that Easter falls on for a given year (defaults to current year) */
PHP_FUNCTION(easter_days)132 PHP_FUNCTION(easter_days)
133 {
134 _cal_easter(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
135 }
136 /* }}} */
137