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