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