xref: /PHP-8.0/ext/calendar/calendar.c (revision 4a2ae841)
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    | http://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    |          Wez Furlong               <wez@thebrainroom.com>            |
17    +----------------------------------------------------------------------+
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "php.h"
25 #include "ext/standard/info.h"
26 #include "calendar_arginfo.h"
27 #include "php_calendar.h"
28 #include "sdncal.h"
29 
30 #include <stdio.h>
31 
32 #ifdef PHP_WIN32
33 /* This conflicts with a define in winnls.h, but that header is needed
34    to have GetACP(). */
35 #undef CAL_GREGORIAN
36 #endif
37 
38 zend_module_entry calendar_module_entry = {
39 	STANDARD_MODULE_HEADER,
40 	"calendar",
41 	ext_functions,
42 	PHP_MINIT(calendar),
43 	NULL,
44 	NULL,
45 	NULL,
46 	PHP_MINFO(calendar),
47 	PHP_CALENDAR_VERSION,
48 	STANDARD_MODULE_PROPERTIES,
49 };
50 
51 #ifdef COMPILE_DL_CALENDAR
52 ZEND_GET_MODULE(calendar)
53 #endif
54 
55 /* this order must match the conversion table below */
56 enum cal_name_type_t {
57 	CAL_GREGORIAN = 0,
58 	CAL_JULIAN,
59 	CAL_JEWISH,
60 	CAL_FRENCH,
61 	CAL_NUM_CALS
62 };
63 
64 typedef zend_long (*cal_to_jd_func_t) (int month, int day, int year);
65 typedef void (*cal_from_jd_func_t) (zend_long jd, int *year, int *month, int *day);
66 typedef char *(*cal_as_string_func_t) (int year, int month, int day);
67 
68 struct cal_entry_t {
69 	const char *name;
70 	const char *symbol;
71 	cal_to_jd_func_t to_jd;
72 	cal_from_jd_func_t from_jd;
73 	int num_months;
74 	int max_days_in_month;
75 	const char * const * month_name_short;
76 	const char * const * month_name_long;
77 };
78 
79 static const struct cal_entry_t cal_conversion_table[CAL_NUM_CALS] = {
80 	{"Gregorian", "CAL_GREGORIAN", GregorianToSdn, SdnToGregorian, 12, 31,
81 	 MonthNameShort, MonthNameLong},
82 	{"Julian", "CAL_JULIAN", JulianToSdn, SdnToJulian, 12, 31,
83 	 MonthNameShort, MonthNameLong},
84 	{"Jewish", "CAL_JEWISH", JewishToSdn, SdnToJewish, 13, 30,
85 	 JewishMonthNameLeap, JewishMonthNameLeap},
86 	{"French", "CAL_FRENCH", FrenchToSdn, SdnToFrench, 13, 30,
87 	 FrenchMonthName, FrenchMonthName}
88 };
89 
90 #define JEWISH_MONTH_NAME(year) 	((monthsPerYear[((year)-1) % 19] == 13)?JewishMonthNameLeap:JewishMonthName)
91 #define JEWISH_HEB_MONTH_NAME(year) ((monthsPerYear[((year)-1) % 19] == 13)?JewishMonthHebNameLeap:JewishMonthHebName)
92 
93 /* For jddayofweek */
94 enum { CAL_DOW_DAYNO, CAL_DOW_LONG, CAL_DOW_SHORT };
95 
96 /* For jdmonthname */
97 enum { CAL_MONTH_GREGORIAN_SHORT, CAL_MONTH_GREGORIAN_LONG,
98 	CAL_MONTH_JULIAN_SHORT, CAL_MONTH_JULIAN_LONG, CAL_MONTH_JEWISH,
99 	CAL_MONTH_FRENCH
100 };
101 
102 /* For heb_number_to_chars escape sequences of אבגדהוזחטיכלמנסעפצקרשת
103    ISO-8859-8 Hebrew alphabet */
104 static const char alef_bet[25] = "0\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEB\xEC\xEE\xF0\xF1\xF2\xF4\xF6\xF7\xF8\xF9\xFA";
105 
106 #define CAL_JEWISH_ADD_ALAFIM_GERESH 0x2
107 #define CAL_JEWISH_ADD_ALAFIM 0x4
108 #define CAL_JEWISH_ADD_GERESHAYIM 0x8
109 
PHP_MINIT_FUNCTION(calendar)110 PHP_MINIT_FUNCTION(calendar)
111 {
112 	REGISTER_LONG_CONSTANT("CAL_GREGORIAN", CAL_GREGORIAN, CONST_CS | CONST_PERSISTENT);
113 	REGISTER_LONG_CONSTANT("CAL_JULIAN", CAL_JULIAN, CONST_CS | CONST_PERSISTENT);
114 	REGISTER_LONG_CONSTANT("CAL_JEWISH", CAL_JEWISH, CONST_CS | CONST_PERSISTENT);
115 	REGISTER_LONG_CONSTANT("CAL_FRENCH", CAL_FRENCH, CONST_CS | CONST_PERSISTENT);
116 	REGISTER_LONG_CONSTANT("CAL_NUM_CALS", CAL_NUM_CALS, CONST_CS | CONST_PERSISTENT);
117 /* constants for jddayofweek */
118 	REGISTER_LONG_CONSTANT("CAL_DOW_DAYNO", CAL_DOW_DAYNO, CONST_CS | CONST_PERSISTENT);
119 	REGISTER_LONG_CONSTANT("CAL_DOW_SHORT", CAL_DOW_SHORT, CONST_CS | CONST_PERSISTENT);
120 	REGISTER_LONG_CONSTANT("CAL_DOW_LONG", CAL_DOW_LONG, CONST_CS | CONST_PERSISTENT);
121 /* constants for jdmonthname */
122 	REGISTER_LONG_CONSTANT("CAL_MONTH_GREGORIAN_SHORT", CAL_MONTH_GREGORIAN_SHORT, CONST_CS | CONST_PERSISTENT);
123 	REGISTER_LONG_CONSTANT("CAL_MONTH_GREGORIAN_LONG", CAL_MONTH_GREGORIAN_LONG, CONST_CS | CONST_PERSISTENT);
124 	REGISTER_LONG_CONSTANT("CAL_MONTH_JULIAN_SHORT", CAL_MONTH_JULIAN_SHORT, CONST_CS | CONST_PERSISTENT);
125 	REGISTER_LONG_CONSTANT("CAL_MONTH_JULIAN_LONG", CAL_MONTH_JULIAN_LONG, CONST_CS | CONST_PERSISTENT);
126 	REGISTER_LONG_CONSTANT("CAL_MONTH_JEWISH", CAL_MONTH_JEWISH, CONST_CS | CONST_PERSISTENT);
127 	REGISTER_LONG_CONSTANT("CAL_MONTH_FRENCH", CAL_MONTH_FRENCH, CONST_CS | CONST_PERSISTENT);
128 /* constants for easter calculation */
129 	REGISTER_LONG_CONSTANT("CAL_EASTER_DEFAULT", CAL_EASTER_DEFAULT, CONST_CS | CONST_PERSISTENT);
130 	REGISTER_LONG_CONSTANT("CAL_EASTER_ROMAN", CAL_EASTER_ROMAN, CONST_CS | CONST_PERSISTENT);
131 	REGISTER_LONG_CONSTANT("CAL_EASTER_ALWAYS_GREGORIAN", CAL_EASTER_ALWAYS_GREGORIAN, CONST_CS | CONST_PERSISTENT);
132 	REGISTER_LONG_CONSTANT("CAL_EASTER_ALWAYS_JULIAN", CAL_EASTER_ALWAYS_JULIAN, CONST_CS | CONST_PERSISTENT);
133 /* constants for Jewish date formatting */
134 	REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_ALAFIM_GERESH", CAL_JEWISH_ADD_ALAFIM_GERESH, CONST_CS | CONST_PERSISTENT);
135 	REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_ALAFIM", CAL_JEWISH_ADD_ALAFIM, CONST_CS | CONST_PERSISTENT);
136 	REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_GERESHAYIM", CAL_JEWISH_ADD_GERESHAYIM, CONST_CS | CONST_PERSISTENT);
137 	return SUCCESS;
138 }
139 
PHP_MINFO_FUNCTION(calendar)140 PHP_MINFO_FUNCTION(calendar)
141 {
142 	php_info_print_table_start();
143 	php_info_print_table_row(2, "Calendar support", "enabled");
144 	php_info_print_table_end();
145 }
146 
_php_cal_info(int cal,zval * ret)147 static void _php_cal_info(int cal, zval *ret)
148 {
149 	zval months, smonths;
150 	int i;
151 	const struct cal_entry_t *calendar;
152 
153 	calendar = &cal_conversion_table[cal];
154 	array_init(ret);
155 
156 	array_init(&months);
157 	array_init(&smonths);
158 
159 	for (i = 1; i <= calendar->num_months; i++) {
160 		add_index_string(&months, i, calendar->month_name_long[i]);
161 		add_index_string(&smonths, i, calendar->month_name_short[i]);
162 	}
163 
164 	add_assoc_zval(ret, "months", &months);
165 	add_assoc_zval(ret, "abbrevmonths", &smonths);
166 	add_assoc_long(ret, "maxdaysinmonth", calendar->max_days_in_month);
167 	add_assoc_string(ret, "calname", calendar->name);
168 	add_assoc_string(ret, "calsymbol", calendar->symbol);
169 
170 }
171 
172 /* {{{ Returns information about a particular calendar */
PHP_FUNCTION(cal_info)173 PHP_FUNCTION(cal_info)
174 {
175 	zend_long cal = -1;
176 
177 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &cal) == FAILURE) {
178 		RETURN_THROWS();
179 	}
180 
181 	if (cal == -1) {
182 		int i;
183 		zval val;
184 
185 		array_init(return_value);
186 
187 		for (i = 0; i < CAL_NUM_CALS; i++) {
188 			_php_cal_info(i, &val);
189 			add_index_zval(return_value, i, &val);
190 		}
191 		return;
192 	}
193 
194 	if (cal < 0 || cal >= CAL_NUM_CALS) {
195 		zend_argument_value_error(1, "must be a valid calendar ID");
196 		RETURN_THROWS();
197 	}
198 
199 	_php_cal_info(cal, return_value);
200 }
201 /* }}} */
202 
203 /* {{{ Returns the number of days in a month for a given year and calendar */
PHP_FUNCTION(cal_days_in_month)204 PHP_FUNCTION(cal_days_in_month)
205 {
206 	zend_long cal, month, year;
207 	const struct cal_entry_t *calendar;
208 	zend_long sdn_start, sdn_next;
209 
210 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &cal, &month, &year) == FAILURE) {
211 		RETURN_THROWS();
212 	}
213 
214 	if (cal < 0 || cal >= CAL_NUM_CALS) {
215 		zend_argument_value_error(1, "must be a valid calendar ID");
216 		RETURN_THROWS();
217 	}
218 
219 	calendar = &cal_conversion_table[cal];
220 
221 	sdn_start = calendar->to_jd(year, month, 1);
222 
223 	if (sdn_start == 0) {
224 		zend_value_error("Invalid date");
225 		RETURN_THROWS();
226 	}
227 
228 	sdn_next = calendar->to_jd(year, 1 + month, 1);
229 
230 	if (sdn_next == 0) {
231 		/* If the next month is invalid, then we need to try the first month of
232 		 * the next year, bearing in mind that the next year after 1 BCE is
233 		 * actually 1 AD and not 0. */
234 		if (year == -1) {
235 			sdn_next = calendar->to_jd(1, 1, 1);
236 		}
237 		else {
238 			sdn_next = calendar->to_jd(year + 1, 1, 1);
239 			if (cal == CAL_FRENCH && sdn_next == 0) {
240 				/* The French calendar ends on 0014-13-05. */
241 				sdn_next = 2380953;
242 			}
243 		}
244 	}
245 
246 	RETURN_LONG(sdn_next - sdn_start);
247 }
248 /* }}} */
249 
250 /* {{{ Converts from a supported calendar to Julian Day Count */
PHP_FUNCTION(cal_to_jd)251 PHP_FUNCTION(cal_to_jd)
252 {
253 	zend_long cal, month, day, year;
254 
255 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "llll", &cal, &month, &day, &year) != SUCCESS) {
256 		RETURN_THROWS();
257 	}
258 
259 	if (cal < 0 || cal >= CAL_NUM_CALS) {
260 		zend_argument_value_error(1, "must be a valid calendar ID");
261 		RETURN_THROWS();
262 	}
263 
264 	RETURN_LONG(cal_conversion_table[cal].to_jd(year, month, day));
265 }
266 /* }}} */
267 
268 /* {{{ Converts from Julian Day Count to a supported calendar and return extended information */
PHP_FUNCTION(cal_from_jd)269 PHP_FUNCTION(cal_from_jd)
270 {
271 	zend_long jd, cal;
272 	int month, day, year, dow;
273 	const struct cal_entry_t *calendar;
274 
275 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &jd, &cal) == FAILURE) {
276 		RETURN_THROWS();
277 	}
278 
279 	if (cal < 0 || cal >= CAL_NUM_CALS) {
280 		zend_argument_value_error(2, "must be a valid calendar ID");
281 		RETURN_THROWS();
282 	}
283 	calendar = &cal_conversion_table[cal];
284 
285 	array_init(return_value);
286 
287 	calendar->from_jd(jd, &year, &month, &day);
288 
289 	add_assoc_str(return_value, "date",
290 		zend_strpprintf(0, "%i/%i/%i", month, day, year));
291 
292 	add_assoc_long(return_value, "month", month);
293 	add_assoc_long(return_value, "day", day);
294 	add_assoc_long(return_value, "year", year);
295 
296 /* day of week */
297 	if (cal != CAL_JEWISH || year > 0) {
298 		dow = DayOfWeek(jd);
299 		add_assoc_long(return_value, "dow", dow);
300 		add_assoc_string(return_value, "abbrevdayname", DayNameShort[dow]);
301 		add_assoc_string(return_value, "dayname", DayNameLong[dow]);
302 	} else {
303 		add_assoc_null(return_value, "dow");
304 		add_assoc_string(return_value, "abbrevdayname", "");
305 		add_assoc_string(return_value, "dayname", "");
306 	}
307 /* month name */
308 	if(cal == CAL_JEWISH) {
309 		/* special case for Jewish calendar */
310 		add_assoc_string(return_value, "abbrevmonth", (year > 0 ? JEWISH_MONTH_NAME(year)[month] : ""));
311 		add_assoc_string(return_value, "monthname", (year > 0 ? JEWISH_MONTH_NAME(year)[month] : ""));
312 	} else {
313 		add_assoc_string(return_value, "abbrevmonth", calendar->month_name_short[month]);
314 		add_assoc_string(return_value, "monthname", calendar->month_name_long[month]);
315 	}
316 }
317 /* }}} */
318 
319 /* {{{ Converts a julian day count to a gregorian calendar date */
PHP_FUNCTION(jdtogregorian)320 PHP_FUNCTION(jdtogregorian)
321 {
322 	zend_long julday;
323 	int year, month, day;
324 
325 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &julday) == FAILURE) {
326 		RETURN_THROWS();
327 	}
328 
329 	SdnToGregorian(julday, &year, &month, &day);
330 
331 	RETURN_NEW_STR(zend_strpprintf(0, "%i/%i/%i", month, day, year));
332 }
333 /* }}} */
334 
335 /* {{{ Converts a gregorian calendar date to julian day count */
PHP_FUNCTION(gregoriantojd)336 PHP_FUNCTION(gregoriantojd)
337 {
338 	zend_long year, month, day;
339 
340 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
341 		RETURN_THROWS();
342 	}
343 
344 	RETURN_LONG(GregorianToSdn(year, month, day));
345 }
346 /* }}} */
347 
348 /* {{{ Convert a julian day count to a julian calendar date */
PHP_FUNCTION(jdtojulian)349 PHP_FUNCTION(jdtojulian)
350 {
351 	zend_long julday;
352 	int year, month, day;
353 
354 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &julday) == FAILURE) {
355 		RETURN_THROWS();
356 	}
357 
358 	SdnToJulian(julday, &year, &month, &day);
359 
360 	RETURN_NEW_STR(zend_strpprintf(0, "%i/%i/%i", month, day, year));
361 }
362 /* }}} */
363 
364 /* {{{ Converts a julian calendar date to julian day count */
PHP_FUNCTION(juliantojd)365 PHP_FUNCTION(juliantojd)
366 {
367 	zend_long year, month, day;
368 
369 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
370 		RETURN_THROWS();
371 	}
372 
373 	RETURN_LONG(JulianToSdn(year, month, day));
374 }
375 /* }}} */
376 
377 /* {{{ heb_number_to_chars*/
378 /*
379 caution: the Hebrew format produces non unique result.
380 for example both: year '5' and year '5000' produce 'ה'.
381 use the numeric one for calculations.
382  */
heb_number_to_chars(int n,int fl,char ** ret)383 static char *heb_number_to_chars(int n, int fl, char **ret)
384 {
385 	char *p, old[18], *endofalafim;
386 
387 	p = endofalafim = old;
388 /*
389    prevents the option breaking the jewish beliefs, and some other
390    critical resources ;)
391  */
392 	if (n > 9999 || n < 1) {
393 		*ret = NULL;
394 		return NULL;
395 	}
396 
397 /* alafim (thousands) case */
398 	if (n / 1000) {
399 		*p = alef_bet[n / 1000];
400 		p++;
401 
402 		if (CAL_JEWISH_ADD_ALAFIM_GERESH & fl) {
403 			*p = '\'';
404 			p++;
405 		}
406 		if (CAL_JEWISH_ADD_ALAFIM & fl) {
407 			/* Escape sequences of Hebrew characters in ISO-8859-8: אלפים */
408 			strcpy(p, " \xE0\xEC\xF4\xE9\xED ");
409 			p += 7;
410 		}
411 
412 		endofalafim = p;
413 		n = n % 1000;
414 	}
415 
416 /* tav-tav (tav=400) case */
417 	while (n >= 400) {
418 		*p = alef_bet[22];
419 		p++;
420 		n -= 400;
421 	}
422 
423 /* meot (hundreads) case */
424 	if (n >= 100) {
425 		*p = alef_bet[18 + n / 100];
426 		p++;
427 		n = n % 100;
428 	}
429 
430 /* tet-vav & tet-zain case (special case for 15 and 16) */
431 	if (n == 15 || n == 16) {
432 		*p = alef_bet[9];
433 		p++;
434 		*p = alef_bet[n - 9];
435 		p++;
436 	} else {
437 /* asarot (tens) case */
438 		if (n >= 10) {
439 			*p = alef_bet[9 + n / 10];
440 			p++;
441 			n = n % 10;
442 		}
443 
444 /* yehidot (ones) case */
445 		if (n > 0) {
446 			*p = alef_bet[n];
447 			p++;
448 		}
449 	}
450 
451 	if (CAL_JEWISH_ADD_GERESHAYIM & fl) {
452 		switch (p - endofalafim) {
453 		case 0:
454 			break;
455 		case 1:
456 			*p = '\'';
457 			p++;
458 			break;
459 		default:
460 			*(p) = *(p - 1);
461 			*(p - 1) = '"';
462 			p++;
463 		}
464 	}
465 
466 	*p = '\0';
467 	*ret = estrndup(old, (p - old) + 1);
468 	p = *ret;
469 	return p;
470 }
471 /* }}} */
472 
473 /* {{{ Converts a julian day count to a jewish calendar date */
PHP_FUNCTION(jdtojewish)474 PHP_FUNCTION(jdtojewish)
475 {
476 	zend_long julday, fl = 0;
477 	zend_bool heb   = 0;
478 	int year, month, day;
479 	char *dayp, *yearp;
480 
481 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|bl", &julday, &heb, &fl) == FAILURE) {
482 		RETURN_THROWS();
483 	}
484 
485 	SdnToJewish(julday, &year, &month, &day);
486 	if (!heb) {
487 		RETURN_NEW_STR(zend_strpprintf(0, "%i/%i/%i", month, day, year));
488 	} else {
489 		if (year <= 0 || year > 9999) {
490 			zend_value_error("Year out of range (0-9999)");
491 			RETURN_THROWS();
492 		}
493 
494 		RETVAL_NEW_STR(zend_strpprintf(0, "%s %s %s", heb_number_to_chars(day, fl, &dayp), JEWISH_HEB_MONTH_NAME(year)[month], heb_number_to_chars(year, fl, &yearp)));
495 
496 		if (dayp) {
497 			efree(dayp);
498 		}
499 		if (yearp) {
500 			efree(yearp);
501 		}
502 	}
503 }
504 /* }}} */
505 
506 /* {{{ Converts a jewish calendar date to a julian day count */
PHP_FUNCTION(jewishtojd)507 PHP_FUNCTION(jewishtojd)
508 {
509 	zend_long year, month, day;
510 
511 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
512 		RETURN_THROWS();
513 	}
514 
515 	RETURN_LONG(JewishToSdn(year, month, day));
516 }
517 /* }}} */
518 
519 /* {{{ Converts a julian day count to a french republic calendar date */
PHP_FUNCTION(jdtofrench)520 PHP_FUNCTION(jdtofrench)
521 {
522 	zend_long julday;
523 	int year, month, day;
524 
525 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &julday) == FAILURE) {
526 		RETURN_THROWS();
527 	}
528 
529 	SdnToFrench(julday, &year, &month, &day);
530 
531 	RETURN_NEW_STR(zend_strpprintf(0, "%i/%i/%i", month, day, year));
532 }
533 /* }}} */
534 
535 /* {{{ Converts a french republic calendar date to julian day count */
PHP_FUNCTION(frenchtojd)536 PHP_FUNCTION(frenchtojd)
537 {
538 	zend_long year, month, day;
539 
540 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &month, &day, &year) == FAILURE) {
541 		RETURN_THROWS();
542 	}
543 
544 	RETURN_LONG(FrenchToSdn(year, month, day));
545 }
546 /* }}} */
547 
548 /* {{{ Returns name or number of day of week from julian day count */
PHP_FUNCTION(jddayofweek)549 PHP_FUNCTION(jddayofweek)
550 {
551 	zend_long julday, mode = CAL_DOW_DAYNO;
552 	int day;
553 	const char *daynamel, *daynames;
554 
555 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &julday, &mode) == FAILURE) {
556 		RETURN_THROWS();
557 	}
558 
559 	day = DayOfWeek(julday);
560 	daynamel = DayNameLong[day];
561 	daynames = DayNameShort[day];
562 
563 	switch (mode) {
564 	case CAL_DOW_LONG:
565 		RETURN_STRING(daynamel);
566 		break;
567 	case CAL_DOW_SHORT:
568 		RETURN_STRING(daynames);
569 		break;
570 	case CAL_DOW_DAYNO:
571 	default:
572 		RETURN_LONG(day);
573 		break;
574 	}
575 }
576 /* }}} */
577 
578 /* {{{ Returns name of month for julian day count */
PHP_FUNCTION(jdmonthname)579 PHP_FUNCTION(jdmonthname)
580 {
581 	zend_long julday, mode;
582 	const char *monthname = NULL;
583 	int month, day, year;
584 
585 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &julday, &mode) == FAILURE) {
586 		RETURN_THROWS();
587 	}
588 
589 	switch (mode) {
590 	case CAL_MONTH_GREGORIAN_LONG:	/* gregorian or julian month */
591 		SdnToGregorian(julday, &year, &month, &day);
592 		monthname = MonthNameLong[month];
593 		break;
594 	case CAL_MONTH_JULIAN_SHORT:	/* gregorian or julian month */
595 		SdnToJulian(julday, &year, &month, &day);
596 		monthname = MonthNameShort[month];
597 		break;
598 	case CAL_MONTH_JULIAN_LONG:	/* gregorian or julian month */
599 		SdnToJulian(julday, &year, &month, &day);
600 		monthname = MonthNameLong[month];
601 		break;
602 	case CAL_MONTH_JEWISH:		/* jewish month */
603 		SdnToJewish(julday, &year, &month, &day);
604 		monthname = (year > 0 ? JEWISH_MONTH_NAME(year)[month] : "");
605 		break;
606 	case CAL_MONTH_FRENCH:		/* french month */
607 		SdnToFrench(julday, &year, &month, &day);
608 		monthname = FrenchMonthName[month];
609 		break;
610 	default:					/* default gregorian */
611 	case CAL_MONTH_GREGORIAN_SHORT:	/* gregorian or julian month */
612 		SdnToGregorian(julday, &year, &month, &day);
613 		monthname = MonthNameShort[month];
614 		break;
615 	}
616 
617 	RETURN_STRING(monthname);
618 }
619 /* }}} */
620