xref: /PHP-7.1/ext/calendar/calendar.c (revision 7f6387b5)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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    |          Wez Furlong               <wez@thebrainroom.com>            |
19    +----------------------------------------------------------------------+
20  */
21 /* $Id$ */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "php.h"
28 #include "ext/standard/info.h"
29 #include "php_calendar.h"
30 #include "sdncal.h"
31 
32 #include <stdio.h>
33 
34 #ifdef PHP_WIN32
35 /* This conflicts with a define in winnls.h, but that header is needed
36    to have GetACP(). */
37 #undef CAL_GREGORIAN
38 #endif
39 
40 /* {{{ arginfo */
41 ZEND_BEGIN_ARG_INFO_EX(arginfo_unixtojd, 0, 0, 0)
42 	ZEND_ARG_INFO(0, timestamp)
43 ZEND_END_ARG_INFO()
44 
45 ZEND_BEGIN_ARG_INFO(arginfo_jdtounix, 0)
46 	ZEND_ARG_INFO(0, jday)
47 ZEND_END_ARG_INFO()
48 
49 ZEND_BEGIN_ARG_INFO_EX(arginfo_cal_info, 0, 0, 0)
50 	ZEND_ARG_INFO(0, calendar)
51 ZEND_END_ARG_INFO()
52 
53 ZEND_BEGIN_ARG_INFO(arginfo_cal_days_in_month, 0)
54 	ZEND_ARG_INFO(0, calendar)
55 	ZEND_ARG_INFO(0, month)
56 	ZEND_ARG_INFO(0, year)
57 ZEND_END_ARG_INFO()
58 
59 ZEND_BEGIN_ARG_INFO(arginfo_cal_to_jd, 0)
60 	ZEND_ARG_INFO(0, calendar)
61 	ZEND_ARG_INFO(0, month)
62 	ZEND_ARG_INFO(0, day)
63 	ZEND_ARG_INFO(0, year)
64 ZEND_END_ARG_INFO()
65 
66 ZEND_BEGIN_ARG_INFO(arginfo_cal_from_jd, 0)
67 	ZEND_ARG_INFO(0, jd)
68 	ZEND_ARG_INFO(0, calendar)
69 ZEND_END_ARG_INFO()
70 
71 ZEND_BEGIN_ARG_INFO(arginfo_jdtogregorian, 0)
72 	ZEND_ARG_INFO(0, juliandaycount)
73 ZEND_END_ARG_INFO()
74 
75 ZEND_BEGIN_ARG_INFO(arginfo_gregoriantojd, 0)
76 	ZEND_ARG_INFO(0, month)
77 	ZEND_ARG_INFO(0, day)
78 	ZEND_ARG_INFO(0, year)
79 ZEND_END_ARG_INFO()
80 
81 ZEND_BEGIN_ARG_INFO(arginfo_jdtojulian, 0)
82 	ZEND_ARG_INFO(0, juliandaycount)
83 ZEND_END_ARG_INFO()
84 
85 ZEND_BEGIN_ARG_INFO(arginfo_juliantojd, 0)
86 	ZEND_ARG_INFO(0, month)
87 	ZEND_ARG_INFO(0, day)
88 	ZEND_ARG_INFO(0, year)
89 ZEND_END_ARG_INFO()
90 
91 ZEND_BEGIN_ARG_INFO_EX(arginfo_jdtojewish, 0, 0, 1)
92 	ZEND_ARG_INFO(0, juliandaycount)
93 	ZEND_ARG_INFO(0, hebrew)
94 	ZEND_ARG_INFO(0, fl)
95 ZEND_END_ARG_INFO()
96 
97 ZEND_BEGIN_ARG_INFO(arginfo_jewishtojd, 0)
98 	ZEND_ARG_INFO(0, month)
99 	ZEND_ARG_INFO(0, day)
100 	ZEND_ARG_INFO(0, year)
101 ZEND_END_ARG_INFO()
102 
103 ZEND_BEGIN_ARG_INFO(arginfo_jdtofrench, 0)
104 	ZEND_ARG_INFO(0, juliandaycount)
105 ZEND_END_ARG_INFO()
106 
107 ZEND_BEGIN_ARG_INFO(arginfo_frenchtojd, 0)
108 	ZEND_ARG_INFO(0, month)
109 	ZEND_ARG_INFO(0, day)
110 	ZEND_ARG_INFO(0, year)
111 ZEND_END_ARG_INFO()
112 
113 ZEND_BEGIN_ARG_INFO_EX(arginfo_jddayofweek, 0, 0, 1)
114 	ZEND_ARG_INFO(0, juliandaycount)
115 	ZEND_ARG_INFO(0, mode)
116 ZEND_END_ARG_INFO()
117 
118 ZEND_BEGIN_ARG_INFO(arginfo_jdmonthname, 0)
119 	ZEND_ARG_INFO(0, juliandaycount)
120 	ZEND_ARG_INFO(0, mode)
121 ZEND_END_ARG_INFO()
122 
123 ZEND_BEGIN_ARG_INFO_EX(arginfo_easter_date, 0, 0, 0)
124 	ZEND_ARG_INFO(0, year)
125 ZEND_END_ARG_INFO()
126 
127 ZEND_BEGIN_ARG_INFO_EX(arginfo_easter_days, 0, 0, 0)
128 	ZEND_ARG_INFO(0, year)
129 	ZEND_ARG_INFO(0, method)
130 ZEND_END_ARG_INFO()
131 
132 /* }}} */
133 
134 const zend_function_entry calendar_functions[] = {
135 	PHP_FE(jdtogregorian, arginfo_jdtogregorian)
136 	PHP_FE(gregoriantojd, arginfo_gregoriantojd)
137 	PHP_FE(jdtojulian, arginfo_jdtojulian)
138 	PHP_FE(juliantojd, arginfo_juliantojd)
139 	PHP_FE(jdtojewish, arginfo_jdtojewish)
140 	PHP_FE(jewishtojd, arginfo_jewishtojd)
141 	PHP_FE(jdtofrench, arginfo_jdtofrench)
142 	PHP_FE(frenchtojd, arginfo_frenchtojd)
143 	PHP_FE(jddayofweek, arginfo_jddayofweek)
144 	PHP_FE(jdmonthname, arginfo_jdmonthname)
145 	PHP_FE(easter_date, arginfo_easter_date)
146 	PHP_FE(easter_days, arginfo_easter_days)
147 	PHP_FE(unixtojd, arginfo_unixtojd)
148 	PHP_FE(jdtounix, arginfo_jdtounix)
149 	PHP_FE(cal_to_jd, arginfo_cal_to_jd)
150 	PHP_FE(cal_from_jd, arginfo_cal_from_jd)
151 	PHP_FE(cal_days_in_month, arginfo_cal_days_in_month)
152 	PHP_FE(cal_info, arginfo_cal_info)
153 	PHP_FE_END
154 };
155 
156 
157 zend_module_entry calendar_module_entry = {
158 	STANDARD_MODULE_HEADER,
159 	"calendar",
160 	calendar_functions,
161 	PHP_MINIT(calendar),
162 	NULL,
163 	NULL,
164 	NULL,
165 	PHP_MINFO(calendar),
166 	PHP_CALENDAR_VERSION,
167 	STANDARD_MODULE_PROPERTIES,
168 };
169 
170 #ifdef COMPILE_DL_CALENDAR
171 ZEND_GET_MODULE(calendar)
172 #endif
173 
174 /* this order must match the conversion table below */
175 enum cal_name_type_t {
176 	CAL_GREGORIAN = 0,
177 	CAL_JULIAN,
178 	CAL_JEWISH,
179 	CAL_FRENCH,
180 	CAL_NUM_CALS
181 };
182 
183 typedef zend_long (*cal_to_jd_func_t) (int month, int day, int year);
184 typedef void (*cal_from_jd_func_t) (zend_long jd, int *year, int *month, int *day);
185 typedef char *(*cal_as_string_func_t) (int year, int month, int day);
186 
187 struct cal_entry_t {
188 	char *name;
189 	char *symbol;
190 	cal_to_jd_func_t to_jd;
191 	cal_from_jd_func_t from_jd;
192 	int num_months;
193 	int max_days_in_month;
194 	char **month_name_short;
195 	char **month_name_long;
196 };
197 
198 static struct cal_entry_t cal_conversion_table[CAL_NUM_CALS] = {
199 	{"Gregorian", "CAL_GREGORIAN", GregorianToSdn, SdnToGregorian, 12, 31,
200 	 MonthNameShort, MonthNameLong},
201 	{"Julian", "CAL_JULIAN", JulianToSdn, SdnToJulian, 12, 31,
202 	 MonthNameShort, MonthNameLong},
203 	{"Jewish", "CAL_JEWISH", JewishToSdn, SdnToJewish, 13, 30,
204 	 JewishMonthNameLeap, JewishMonthNameLeap},
205 	{"French", "CAL_FRENCH", FrenchToSdn, SdnToFrench, 13, 30,
206 	 FrenchMonthName, FrenchMonthName}
207 };
208 
209 #define JEWISH_MONTH_NAME(year) 	((monthsPerYear[((year)-1) % 19] == 13)?JewishMonthNameLeap:JewishMonthName)
210 #define JEWISH_HEB_MONTH_NAME(year) ((monthsPerYear[((year)-1) % 19] == 13)?JewishMonthHebNameLeap:JewishMonthHebName)
211 
212 /* For jddayofweek */
213 enum { CAL_DOW_DAYNO, CAL_DOW_LONG, CAL_DOW_SHORT };
214 
215 /* For jdmonthname */
216 enum { CAL_MONTH_GREGORIAN_SHORT, CAL_MONTH_GREGORIAN_LONG,
217 	CAL_MONTH_JULIAN_SHORT, CAL_MONTH_JULIAN_LONG, CAL_MONTH_JEWISH,
218 	CAL_MONTH_FRENCH
219 };
220 
221 /* for heb_number_to_chars */
222 static char alef_bet[25] = "0����������������������";
223 
224 #define CAL_JEWISH_ADD_ALAFIM_GERESH 0x2
225 #define CAL_JEWISH_ADD_ALAFIM 0x4
226 #define CAL_JEWISH_ADD_GERESHAYIM 0x8
227 
PHP_MINIT_FUNCTION(calendar)228 PHP_MINIT_FUNCTION(calendar)
229 {
230 	REGISTER_LONG_CONSTANT("CAL_GREGORIAN", CAL_GREGORIAN, CONST_CS | CONST_PERSISTENT);
231 	REGISTER_LONG_CONSTANT("CAL_JULIAN", CAL_JULIAN, CONST_CS | CONST_PERSISTENT);
232 	REGISTER_LONG_CONSTANT("CAL_JEWISH", CAL_JEWISH, CONST_CS | CONST_PERSISTENT);
233 	REGISTER_LONG_CONSTANT("CAL_FRENCH", CAL_FRENCH, CONST_CS | CONST_PERSISTENT);
234 	REGISTER_LONG_CONSTANT("CAL_NUM_CALS", CAL_NUM_CALS, CONST_CS | CONST_PERSISTENT);
235 /* constants for jddayofweek */
236 	REGISTER_LONG_CONSTANT("CAL_DOW_DAYNO", CAL_DOW_DAYNO, CONST_CS | CONST_PERSISTENT);
237 	REGISTER_LONG_CONSTANT("CAL_DOW_SHORT", CAL_DOW_SHORT, CONST_CS | CONST_PERSISTENT);
238 	REGISTER_LONG_CONSTANT("CAL_DOW_LONG", CAL_DOW_LONG, CONST_CS | CONST_PERSISTENT);
239 /* constants for jdmonthname */
240 	REGISTER_LONG_CONSTANT("CAL_MONTH_GREGORIAN_SHORT", CAL_MONTH_GREGORIAN_SHORT, CONST_CS | CONST_PERSISTENT);
241 	REGISTER_LONG_CONSTANT("CAL_MONTH_GREGORIAN_LONG", CAL_MONTH_GREGORIAN_LONG, CONST_CS | CONST_PERSISTENT);
242 	REGISTER_LONG_CONSTANT("CAL_MONTH_JULIAN_SHORT", CAL_MONTH_JULIAN_SHORT, CONST_CS | CONST_PERSISTENT);
243 	REGISTER_LONG_CONSTANT("CAL_MONTH_JULIAN_LONG", CAL_MONTH_JULIAN_LONG, CONST_CS | CONST_PERSISTENT);
244 	REGISTER_LONG_CONSTANT("CAL_MONTH_JEWISH", CAL_MONTH_JEWISH, CONST_CS | CONST_PERSISTENT);
245 	REGISTER_LONG_CONSTANT("CAL_MONTH_FRENCH", CAL_MONTH_FRENCH, CONST_CS | CONST_PERSISTENT);
246 /* constants for easter calculation */
247 	REGISTER_LONG_CONSTANT("CAL_EASTER_DEFAULT", CAL_EASTER_DEFAULT, CONST_CS | CONST_PERSISTENT);
248 	REGISTER_LONG_CONSTANT("CAL_EASTER_ROMAN", CAL_EASTER_ROMAN, CONST_CS | CONST_PERSISTENT);
249 	REGISTER_LONG_CONSTANT("CAL_EASTER_ALWAYS_GREGORIAN", CAL_EASTER_ALWAYS_GREGORIAN, CONST_CS | CONST_PERSISTENT);
250 	REGISTER_LONG_CONSTANT("CAL_EASTER_ALWAYS_JULIAN", CAL_EASTER_ALWAYS_JULIAN, CONST_CS | CONST_PERSISTENT);
251 /* constants for Jewish date formatting */
252 	REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_ALAFIM_GERESH", CAL_JEWISH_ADD_ALAFIM_GERESH, CONST_CS | CONST_PERSISTENT);
253 	REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_ALAFIM", CAL_JEWISH_ADD_ALAFIM, CONST_CS | CONST_PERSISTENT);
254 	REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_GERESHAYIM", CAL_JEWISH_ADD_GERESHAYIM, CONST_CS | CONST_PERSISTENT);
255 	return SUCCESS;
256 }
257 
PHP_MINFO_FUNCTION(calendar)258 PHP_MINFO_FUNCTION(calendar)
259 {
260 	php_info_print_table_start();
261 	php_info_print_table_row(2, "Calendar support", "enabled");
262 	php_info_print_table_end();
263 }
264 
_php_cal_info(int cal,zval * ret)265 static void _php_cal_info(int cal, zval *ret)
266 {
267 	zval months, smonths;
268 	int i;
269 	struct cal_entry_t *calendar;
270 
271 	calendar = &cal_conversion_table[cal];
272 	array_init(ret);
273 
274 	array_init(&months);
275 	array_init(&smonths);
276 
277 	for (i = 1; i <= calendar->num_months; i++) {
278 		add_index_string(&months, i, calendar->month_name_long[i]);
279 		add_index_string(&smonths, i, calendar->month_name_short[i]);
280 	}
281 
282 	add_assoc_zval(ret, "months", &months);
283 	add_assoc_zval(ret, "abbrevmonths", &smonths);
284 	add_assoc_long(ret, "maxdaysinmonth", calendar->max_days_in_month);
285 	add_assoc_string(ret, "calname", calendar->name);
286 	add_assoc_string(ret, "calsymbol", calendar->symbol);
287 
288 }
289 
290 /* {{{ proto array cal_info([int calendar])
291    Returns information about a particular calendar */
PHP_FUNCTION(cal_info)292 PHP_FUNCTION(cal_info)
293 {
294 	zend_long cal = -1;
295 
296 
297 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &cal) == FAILURE) {
298 		RETURN_FALSE;
299 	}
300 
301 	if (cal == -1) {
302 		int i;
303 		zval val;
304 
305 		array_init(return_value);
306 
307 		for (i = 0; i < CAL_NUM_CALS; i++) {
308 			_php_cal_info(i, &val);
309 			add_index_zval(return_value, i, &val);
310 		}
311 		return;
312 	}
313 
314 
315 	if (cal != -1 && (cal < 0 || cal >= CAL_NUM_CALS)) {
316 		php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT ".", cal);
317 		RETURN_FALSE;
318 	}
319 
320 	_php_cal_info(cal, return_value);
321 
322 }
323 /* }}} */
324 
325 /* {{{ proto int cal_days_in_month(int calendar, int month, int year)
326    Returns the number of days in a month for a given year and calendar */
PHP_FUNCTION(cal_days_in_month)327 PHP_FUNCTION(cal_days_in_month)
328 {
329 	zend_long cal, month, year;
330 	struct cal_entry_t *calendar;
331 	zend_long sdn_start, sdn_next;
332 
333 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &cal, &month, &year) == FAILURE) {
334 		RETURN_FALSE;
335 	}
336 
337 	if (cal < 0 || cal >= CAL_NUM_CALS) {
338 		php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT ".", cal);
339 		RETURN_FALSE;
340 	}
341 
342 	calendar = &cal_conversion_table[cal];
343 
344 	sdn_start = calendar->to_jd(year, month, 1);
345 
346 	if (sdn_start == 0) {
347 		php_error_docref(NULL, E_WARNING, "invalid date.");
348 		RETURN_FALSE;
349 	}
350 
351 	sdn_next = calendar->to_jd(year, 1 + month, 1);
352 
353 	if (sdn_next == 0) {
354 		/* If the next month is invalid, then we need to try the first month of
355 		 * the next year, bearing in mind that the next year after 1 BCE is
356 		 * actually 1 AD and not 0. */
357 		if (year == -1) {
358 			sdn_next = calendar->to_jd(1, 1, 1);
359 		}
360 		else {
361 			sdn_next = calendar->to_jd(year + 1, 1, 1);
362 			if (cal == CAL_FRENCH && sdn_next == 0) {
363 				/* The French calendar ends on 0014-13-05. */
364 				sdn_next = 2380953;
365 			}
366 		}
367 	}
368 
369 	RETURN_LONG(sdn_next - sdn_start);
370 }
371 /* }}} */
372 
373 /* {{{ proto int cal_to_jd(int calendar, int month, int day, int year)
374    Converts from a supported calendar to Julian Day Count */
PHP_FUNCTION(cal_to_jd)375 PHP_FUNCTION(cal_to_jd)
376 {
377 	zend_long cal, month, day, year;
378 
379 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "llll", &cal, &month, &day, &year) != SUCCESS) {
380 		RETURN_FALSE;
381 	}
382 
383 	if (cal < 0 || cal >= CAL_NUM_CALS) {
384 		php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT ".", cal);
385 		RETURN_FALSE;
386 	}
387 
388 	RETURN_LONG(cal_conversion_table[cal].to_jd(year, month, day));
389 }
390 /* }}} */
391 
392 /* {{{ proto array cal_from_jd(int jd, int calendar)
393    Converts from Julian Day Count to a supported calendar and return extended information */
PHP_FUNCTION(cal_from_jd)394 PHP_FUNCTION(cal_from_jd)
395 {
396 	zend_long jd, cal;
397 	int month, day, year, dow;
398 	char date[16];
399 	struct cal_entry_t *calendar;
400 
401 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &jd, &cal) == FAILURE) {
402 		RETURN_FALSE;
403 	}
404 
405 	if (cal < 0 || cal >= CAL_NUM_CALS) {
406 		php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT "", cal);
407 		RETURN_FALSE;
408 	}
409 	calendar = &cal_conversion_table[cal];
410 
411 	array_init(return_value);
412 
413 	calendar->from_jd(jd, &year, &month, &day);
414 
415 	snprintf(date, sizeof(date), "%i/%i/%i", month, day, year);
416 	add_assoc_string(return_value, "date", date);
417 
418 	add_assoc_long(return_value, "month", month);
419 	add_assoc_long(return_value, "day", day);
420 	add_assoc_long(return_value, "year", year);
421 
422 /* day of week */
423 	if (cal != CAL_JEWISH || year > 0) {
424 		dow = DayOfWeek(jd);
425 		add_assoc_long(return_value, "dow", dow);
426 		add_assoc_string(return_value, "abbrevdayname", DayNameShort[dow]);
427 		add_assoc_string(return_value, "dayname", DayNameLong[dow]);
428 	} else {
429 		add_assoc_null(return_value, "dow");
430 		add_assoc_string(return_value, "abbrevdayname", "");
431 		add_assoc_string(return_value, "dayname", "");
432 	}
433 /* month name */
434 	if(cal == CAL_JEWISH) {
435 		/* special case for Jewish calendar */
436 		add_assoc_string(return_value, "abbrevmonth", (year > 0 ? JEWISH_MONTH_NAME(year)[month] : ""));
437 		add_assoc_string(return_value, "monthname", (year > 0 ? JEWISH_MONTH_NAME(year)[month] : ""));
438 	} else {
439 		add_assoc_string(return_value, "abbrevmonth", calendar->month_name_short[month]);
440 		add_assoc_string(return_value, "monthname", calendar->month_name_long[month]);
441 	}
442 }
443 /* }}} */
444 
445 /* {{{ proto string jdtogregorian(int juliandaycount)
446    Converts a julian day count to a gregorian calendar date */
PHP_FUNCTION(jdtogregorian)447 PHP_FUNCTION(jdtogregorian)
448 {
449 	zend_long julday;
450 	int year, month, day;
451 	char date[16];
452 
453 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &julday) == FAILURE) {
454 		RETURN_FALSE;
455 	}
456 
457 	SdnToGregorian(julday, &year, &month, &day);
458 	snprintf(date, sizeof(date), "%i/%i/%i", month, day, year);
459 
460 	RETURN_STRING(date);
461 }
462 /* }}} */
463 
464 /* {{{ proto int gregoriantojd(int month, int day, int year)
465    Converts a gregorian calendar date to julian day count */
PHP_FUNCTION(gregoriantojd)466 PHP_FUNCTION(gregoriantojd)
467 {
468 	zend_long year, month, day;
469 
470 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
471 		RETURN_FALSE;
472 	}
473 
474 	RETURN_LONG(GregorianToSdn(year, month, day));
475 }
476 /* }}} */
477 
478 /* {{{ proto string jdtojulian(int juliandaycount)
479    Convert a julian day count to a julian calendar date */
PHP_FUNCTION(jdtojulian)480 PHP_FUNCTION(jdtojulian)
481 {
482 	zend_long julday;
483 	int year, month, day;
484 	char date[16];
485 
486 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &julday) == FAILURE) {
487 		RETURN_FALSE;
488 	}
489 
490 	SdnToJulian(julday, &year, &month, &day);
491 	snprintf(date, sizeof(date), "%i/%i/%i", month, day, year);
492 
493 	RETURN_STRING(date);
494 }
495 /* }}} */
496 
497 /* {{{ proto int juliantojd(int month, int day, int year)
498    Converts a julian calendar date to julian day count */
PHP_FUNCTION(juliantojd)499 PHP_FUNCTION(juliantojd)
500 {
501 	zend_long year, month, day;
502 
503 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
504 		RETURN_FALSE;
505 	}
506 
507 	RETURN_LONG(JulianToSdn(year, month, day));
508 }
509 /* }}} */
510 
511 /* {{{ heb_number_to_chars*/
512 /*
513 caution: the Hebrew format produces non unique result.
514 for example both: year '5' and year '5000' produce '�'.
515 use the numeric one for calculations.
516  */
heb_number_to_chars(int n,int fl,char ** ret)517 static char *heb_number_to_chars(int n, int fl, char **ret)
518 {
519 	char *p, old[18], *endofalafim;
520 
521 	p = endofalafim = old;
522 /*
523    prevents the option breaking the jewish beliefs, and some other
524    critical resources ;)
525  */
526 	if (n > 9999 || n < 1) {
527 		*ret = NULL;
528 		return NULL;
529 	}
530 
531 /* alafim (thousands) case */
532 	if (n / 1000) {
533 		*p = alef_bet[n / 1000];
534 		p++;
535 
536 		if (CAL_JEWISH_ADD_ALAFIM_GERESH & fl) {
537 			*p = '\'';
538 			p++;
539 		}
540 		if (CAL_JEWISH_ADD_ALAFIM & fl) {
541 			strcpy(p, " ����� ");
542 			p += 7;
543 		}
544 
545 		endofalafim = p;
546 		n = n % 1000;
547 	}
548 
549 /* tav-tav (tav=400) case */
550 	while (n >= 400) {
551 		*p = alef_bet[22];
552 		p++;
553 		n -= 400;
554 	}
555 
556 /* meot (hundreads) case */
557 	if (n >= 100) {
558 		*p = alef_bet[18 + n / 100];
559 		p++;
560 		n = n % 100;
561 	}
562 
563 /* tet-vav & tet-zain case (special case for 15 and 16) */
564 	if (n == 15 || n == 16) {
565 		*p = alef_bet[9];
566 		p++;
567 		*p = alef_bet[n - 9];
568 		p++;
569 	} else {
570 /* asarot (tens) case */
571 		if (n >= 10) {
572 			*p = alef_bet[9 + n / 10];
573 			p++;
574 			n = n % 10;
575 		}
576 
577 /* yehidot (ones) case */
578 		if (n > 0) {
579 			*p = alef_bet[n];
580 			p++;
581 		}
582 	}
583 
584 	if (CAL_JEWISH_ADD_GERESHAYIM & fl) {
585 		switch (p - endofalafim) {
586 		case 0:
587 			break;
588 		case 1:
589 			*p = '\'';
590 			p++;
591 			break;
592 		default:
593 			*(p) = *(p - 1);
594 			*(p - 1) = '"';
595 			p++;
596 		}
597 	}
598 
599 	*p = '\0';
600 	*ret = estrndup(old, (p - old) + 1);
601 	p = *ret;
602 	return p;
603 }
604 /* }}} */
605 
606 /* {{{ proto string jdtojewish(int juliandaycount [, bool hebrew [, int fl]])
607    Converts a julian day count to a jewish calendar date */
PHP_FUNCTION(jdtojewish)608 PHP_FUNCTION(jdtojewish)
609 {
610 	zend_long julday, fl = 0;
611 	zend_bool heb   = 0;
612 	int year, month, day;
613 	char date[16], hebdate[32];
614 	char *dayp, *yearp;
615 
616 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|bl", &julday, &heb, &fl) == FAILURE) {
617 		RETURN_FALSE;
618 	}
619 
620 	SdnToJewish(julday, &year, &month, &day);
621 	if (!heb) {
622 		snprintf(date, sizeof(date), "%i/%i/%i", month, day, year);
623 		RETURN_STRING(date);
624 	} else {
625 		if (year <= 0 || year > 9999) {
626 			php_error_docref(NULL, E_WARNING, "Year out of range (0-9999).");
627 			RETURN_FALSE;
628 		}
629 
630 		snprintf(hebdate, sizeof(hebdate), "%s %s %s", heb_number_to_chars(day, fl, &dayp), JEWISH_HEB_MONTH_NAME(year)[month], heb_number_to_chars(year, fl, &yearp));
631 
632 		if (dayp) {
633 			efree(dayp);
634 		}
635 		if (yearp) {
636 			efree(yearp);
637 		}
638 
639 		RETURN_STRING(hebdate);
640 
641 	}
642 }
643 /* }}} */
644 
645 /* {{{ proto int jewishtojd(int month, int day, int year)
646    Converts a jewish calendar date to a julian day count */
PHP_FUNCTION(jewishtojd)647 PHP_FUNCTION(jewishtojd)
648 {
649 	zend_long year, month, day;
650 
651 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
652 		RETURN_FALSE;
653 	}
654 
655 	RETURN_LONG(JewishToSdn(year, month, day));
656 }
657 /* }}} */
658 
659 /* {{{ proto string jdtofrench(int juliandaycount)
660    Converts a julian day count to a french republic calendar date */
PHP_FUNCTION(jdtofrench)661 PHP_FUNCTION(jdtofrench)
662 {
663 	zend_long julday;
664 	int year, month, day;
665 	char date[16];
666 
667 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &julday) == FAILURE) {
668 		RETURN_FALSE;
669 	}
670 
671 	SdnToFrench(julday, &year, &month, &day);
672 	snprintf(date, sizeof(date), "%i/%i/%i", month, day, year);
673 
674 	RETURN_STRING(date);
675 }
676 /* }}} */
677 
678 /* {{{ proto int frenchtojd(int month, int day, int year)
679    Converts a french republic calendar date to julian day count */
PHP_FUNCTION(frenchtojd)680 PHP_FUNCTION(frenchtojd)
681 {
682 	zend_long year, month, day;
683 
684 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
685 		RETURN_FALSE;
686 	}
687 
688 	RETURN_LONG(FrenchToSdn(year, month, day));
689 }
690 /* }}} */
691 
692 /* {{{ proto mixed jddayofweek(int juliandaycount [, int mode])
693    Returns name or number of day of week from julian day count */
PHP_FUNCTION(jddayofweek)694 PHP_FUNCTION(jddayofweek)
695 {
696 	zend_long julday, mode = CAL_DOW_DAYNO;
697 	int day;
698 	char *daynamel, *daynames;
699 
700 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &julday, &mode) == FAILURE) {
701 		RETURN_FALSE;
702 	}
703 
704 	day = DayOfWeek(julday);
705 	daynamel = DayNameLong[day];
706 	daynames = DayNameShort[day];
707 
708 	switch (mode) {
709 	case CAL_DOW_LONG:
710 		RETURN_STRING(daynamel);
711 		break;
712 	case CAL_DOW_SHORT:
713 		RETURN_STRING(daynames);
714 		break;
715 	case CAL_DOW_DAYNO:
716 	default:
717 		RETURN_LONG(day);
718 		break;
719 	}
720 }
721 /* }}} */
722 
723 /* {{{ proto string jdmonthname(int juliandaycount, int mode)
724    Returns name of month for julian day count */
PHP_FUNCTION(jdmonthname)725 PHP_FUNCTION(jdmonthname)
726 {
727 	zend_long julday, mode;
728 	char *monthname = NULL;
729 	int month, day, year;
730 
731 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &julday, &mode) == FAILURE) {
732 		RETURN_FALSE;
733 	}
734 
735 	switch (mode) {
736 	case CAL_MONTH_GREGORIAN_LONG:	/* gregorian or julian month */
737 		SdnToGregorian(julday, &year, &month, &day);
738 		monthname = MonthNameLong[month];
739 		break;
740 	case CAL_MONTH_JULIAN_SHORT:	/* gregorian or julian month */
741 		SdnToJulian(julday, &year, &month, &day);
742 		monthname = MonthNameShort[month];
743 		break;
744 	case CAL_MONTH_JULIAN_LONG:	/* gregorian or julian month */
745 		SdnToJulian(julday, &year, &month, &day);
746 		monthname = MonthNameLong[month];
747 		break;
748 	case CAL_MONTH_JEWISH:		/* jewish month */
749 		SdnToJewish(julday, &year, &month, &day);
750 		monthname = (year > 0 ? JEWISH_MONTH_NAME(year)[month] : "");
751 		break;
752 	case CAL_MONTH_FRENCH:		/* french month */
753 		SdnToFrench(julday, &year, &month, &day);
754 		monthname = FrenchMonthName[month];
755 		break;
756 	default:					/* default gregorian */
757 	case CAL_MONTH_GREGORIAN_SHORT:	/* gregorian or julian month */
758 		SdnToGregorian(julday, &year, &month, &day);
759 		monthname = MonthNameShort[month];
760 		break;
761 	}
762 
763 	RETURN_STRING(monthname);
764 }
765 /* }}} */
766 
767 /*
768  * Local variables:
769  * tab-width: 4
770  * c-basic-offset: 4
771  * End:
772  * vim600: sw=4 ts=4 fdm=marker
773  * vim<600: sw=4 ts=4
774  */
775