diff options
Diffstat (limited to 'ext/calendar')
39 files changed, 3997 insertions, 0 deletions
diff --git a/ext/calendar/CREDITS b/ext/calendar/CREDITS new file mode 100644 index 0000000..a2904bd --- /dev/null +++ b/ext/calendar/CREDITS @@ -0,0 +1,2 @@ +Calendar +Shane Caraveo, Colin Viebrock, Hartmut Holzgraefe, Wez Furlong diff --git a/ext/calendar/cal_unix.c b/ext/calendar/cal_unix.c new file mode 100644 index 0000000..c65b046 --- /dev/null +++ b/ext/calendar/cal_unix.c @@ -0,0 +1,78 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Shane Caraveo <shane@caraveo.com> | + | Colin Viebrock <colin@easydns.com> | + | Hartmut Holzgraefe <hholzgra@php.net> | + +----------------------------------------------------------------------+ + */ +/* $Id: */ + +#include "php.h" +#include "php_calendar.h" +#include "sdncal.h" +#include <time.h> + +/* {{{ proto int unixtojd([int timestamp]) + Convert UNIX timestamp to Julian Day */ +PHP_FUNCTION(unixtojd) +{ + time_t ts = 0; + struct tm *ta, tmbuf; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts) == FAILURE) { + return; + } + + if (!ts) { + ts = time(NULL); + } else if (ts < 0) { + RETURN_FALSE; + } + + if (!(ta = php_localtime_r(&ts, &tmbuf))) { + RETURN_FALSE; + } + + RETURN_LONG(GregorianToSdn(ta->tm_year+1900, ta->tm_mon+1, ta->tm_mday)); +} +/* }}} */ + +/* {{{ proto int jdtounix(int jday) + Convert Julian Day to UNIX timestamp */ +PHP_FUNCTION(jdtounix) +{ + long uday; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &uday) == FAILURE) { + return; + } + uday -= 2440588 /* J.D. of 1.1.1970 */; + + if (uday < 0 || uday > 24755) { /* before beginning of unix epoch or behind end of unix epoch */ + RETURN_FALSE; + } + + RETURN_LONG(uday * 24 * 3600); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/calendar/calendar.c b/ext/calendar/calendar.c new file mode 100644 index 0000000..010b8d4 --- /dev/null +++ b/ext/calendar/calendar.c @@ -0,0 +1,755 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Shane Caraveo <shane@caraveo.com> | + | Colin Viebrock <colin@easydns.com> | + | Hartmut Holzgraefe <hholzgra@php.net> | + | Wez Furlong <wez@thebrainroom.com> | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef PHP_WIN32 +#define _WINNLS_ +#endif + +#include "php.h" +#include "ext/standard/info.h" +#include "php_calendar.h" +#include "sdncal.h" + +#include <stdio.h> + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_unixtojd, 0, 0, 0) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_jdtounix, 0) + ZEND_ARG_INFO(0, jday) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_cal_info, 0, 0, 0) + ZEND_ARG_INFO(0, calendar) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_cal_days_in_month, 0) + ZEND_ARG_INFO(0, calendar) + ZEND_ARG_INFO(0, month) + ZEND_ARG_INFO(0, year) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_cal_to_jd, 0) + ZEND_ARG_INFO(0, calendar) + ZEND_ARG_INFO(0, month) + ZEND_ARG_INFO(0, day) + ZEND_ARG_INFO(0, year) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_cal_from_jd, 0) + ZEND_ARG_INFO(0, jd) + ZEND_ARG_INFO(0, calendar) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_jdtogregorian, 0) + ZEND_ARG_INFO(0, juliandaycount) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_gregoriantojd, 0) + ZEND_ARG_INFO(0, month) + ZEND_ARG_INFO(0, day) + ZEND_ARG_INFO(0, year) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_jdtojulian, 0) + ZEND_ARG_INFO(0, juliandaycount) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_juliantojd, 0) + ZEND_ARG_INFO(0, month) + ZEND_ARG_INFO(0, day) + ZEND_ARG_INFO(0, year) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_jdtojewish, 0, 0, 1) + ZEND_ARG_INFO(0, juliandaycount) + ZEND_ARG_INFO(0, hebrew) + ZEND_ARG_INFO(0, fl) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_jewishtojd, 0) + ZEND_ARG_INFO(0, month) + ZEND_ARG_INFO(0, day) + ZEND_ARG_INFO(0, year) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_jdtofrench, 0) + ZEND_ARG_INFO(0, juliandaycount) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_frenchtojd, 0) + ZEND_ARG_INFO(0, month) + ZEND_ARG_INFO(0, day) + ZEND_ARG_INFO(0, year) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_jddayofweek, 0, 0, 1) + ZEND_ARG_INFO(0, juliandaycount) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_jdmonthname, 0) + ZEND_ARG_INFO(0, juliandaycount) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_easter_date, 0, 0, 0) + ZEND_ARG_INFO(0, year) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_easter_days, 0, 0, 0) + ZEND_ARG_INFO(0, year) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +/* }}} */ + +const zend_function_entry calendar_functions[] = { + PHP_FE(jdtogregorian, arginfo_jdtogregorian) + PHP_FE(gregoriantojd, arginfo_gregoriantojd) + PHP_FE(jdtojulian, arginfo_jdtojulian) + PHP_FE(juliantojd, arginfo_juliantojd) + PHP_FE(jdtojewish, arginfo_jdtojewish) + PHP_FE(jewishtojd, arginfo_jewishtojd) + PHP_FE(jdtofrench, arginfo_jdtofrench) + PHP_FE(frenchtojd, arginfo_frenchtojd) + PHP_FE(jddayofweek, arginfo_jddayofweek) + PHP_FE(jdmonthname, arginfo_jdmonthname) + PHP_FE(easter_date, arginfo_easter_date) + PHP_FE(easter_days, arginfo_easter_days) + PHP_FE(unixtojd, arginfo_unixtojd) + PHP_FE(jdtounix, arginfo_jdtounix) + PHP_FE(cal_to_jd, arginfo_cal_to_jd) + PHP_FE(cal_from_jd, arginfo_cal_from_jd) + PHP_FE(cal_days_in_month, arginfo_cal_days_in_month) + PHP_FE(cal_info, arginfo_cal_info) + PHP_FE_END +}; + + +zend_module_entry calendar_module_entry = { + STANDARD_MODULE_HEADER, + "calendar", + calendar_functions, + PHP_MINIT(calendar), + NULL, + NULL, + NULL, + PHP_MINFO(calendar), + NO_VERSION_YET, + STANDARD_MODULE_PROPERTIES, +}; + +#ifdef COMPILE_DL_CALENDAR +ZEND_GET_MODULE(calendar) +#endif + +/* this order must match the conversion table below */ +enum cal_name_type_t { + CAL_GREGORIAN = 0, + CAL_JULIAN, + CAL_JEWISH, + CAL_FRENCH, + CAL_NUM_CALS +}; + +typedef long int (*cal_to_jd_func_t) (int month, int day, int year); +typedef void (*cal_from_jd_func_t) (long int jd, int *year, int *month, int *day); +typedef char *(*cal_as_string_func_t) (int year, int month, int day); + +struct cal_entry_t { + char *name; + char *symbol; + cal_to_jd_func_t to_jd; + cal_from_jd_func_t from_jd; + int num_months; + int max_days_in_month; + char **month_name_short; + char **month_name_long; +}; + +static struct cal_entry_t cal_conversion_table[CAL_NUM_CALS] = { + {"Gregorian", "CAL_GREGORIAN", GregorianToSdn, SdnToGregorian, 12, 31, + MonthNameShort, MonthNameLong}, + {"Julian", "CAL_JULIAN", JulianToSdn, SdnToJulian, 12, 31, + MonthNameShort, MonthNameLong}, + {"Jewish", "CAL_JEWISH", JewishToSdn, SdnToJewish, 13, 30, + JewishMonthName, JewishMonthName}, + {"French", "CAL_FRENCH", FrenchToSdn, SdnToFrench, 13, 30, + FrenchMonthName, FrenchMonthName} +}; + +/* For jddayofweek */ +enum { CAL_DOW_DAYNO, CAL_DOW_SHORT, CAL_DOW_LONG }; + +/* For jdmonthname */ +enum { CAL_MONTH_GREGORIAN_SHORT, CAL_MONTH_GREGORIAN_LONG, + CAL_MONTH_JULIAN_SHORT, CAL_MONTH_JULIAN_LONG, CAL_MONTH_JEWISH, + CAL_MONTH_FRENCH +}; + +/* for heb_number_to_chars */ +static char alef_bet[25] = "0אבגדהוזחטיכלמנסעפצקרשת"; + +#define CAL_JEWISH_ADD_ALAFIM_GERESH 0x2 +#define CAL_JEWISH_ADD_ALAFIM 0x4 +#define CAL_JEWISH_ADD_GERESHAYIM 0x8 + +PHP_MINIT_FUNCTION(calendar) +{ + REGISTER_LONG_CONSTANT("CAL_GREGORIAN", CAL_GREGORIAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_JULIAN", CAL_JULIAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_JEWISH", CAL_JEWISH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_FRENCH", CAL_FRENCH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_NUM_CALS", CAL_NUM_CALS, CONST_CS | CONST_PERSISTENT); +/* constants for jddayofweek */ + REGISTER_LONG_CONSTANT("CAL_DOW_DAYNO", CAL_DOW_DAYNO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_DOW_SHORT", CAL_DOW_SHORT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_DOW_LONG", CAL_DOW_LONG, CONST_CS | CONST_PERSISTENT); +/* constants for jdmonthname */ + REGISTER_LONG_CONSTANT("CAL_MONTH_GREGORIAN_SHORT", CAL_MONTH_GREGORIAN_SHORT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_MONTH_GREGORIAN_LONG", CAL_MONTH_GREGORIAN_LONG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_MONTH_JULIAN_SHORT", CAL_MONTH_JULIAN_SHORT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_MONTH_JULIAN_LONG", CAL_MONTH_JULIAN_LONG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_MONTH_JEWISH", CAL_MONTH_JEWISH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_MONTH_FRENCH", CAL_MONTH_FRENCH, CONST_CS | CONST_PERSISTENT); +/* constants for easter calculation */ + REGISTER_LONG_CONSTANT("CAL_EASTER_DEFAULT", CAL_EASTER_DEFAULT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_EASTER_ROMAN", CAL_EASTER_ROMAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_EASTER_ALWAYS_GREGORIAN", CAL_EASTER_ALWAYS_GREGORIAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_EASTER_ALWAYS_JULIAN", CAL_EASTER_ALWAYS_JULIAN, CONST_CS | CONST_PERSISTENT); +/* constants for Jewish date formatting */ + REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_ALAFIM_GERESH", CAL_JEWISH_ADD_ALAFIM_GERESH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_ALAFIM", CAL_JEWISH_ADD_ALAFIM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CAL_JEWISH_ADD_GERESHAYIM", CAL_JEWISH_ADD_GERESHAYIM, CONST_CS | CONST_PERSISTENT); + return SUCCESS; +} + +PHP_MINFO_FUNCTION(calendar) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "Calendar support", "enabled"); + php_info_print_table_end(); +} + +static void _php_cal_info(int cal, zval **ret) +{ + zval *months, *smonths; + int i; + struct cal_entry_t *calendar; + + calendar = &cal_conversion_table[cal]; + array_init(*ret); + + MAKE_STD_ZVAL(months); + MAKE_STD_ZVAL(smonths); + array_init(months); + array_init(smonths); + + for (i = 1; i <= calendar->num_months; i++) { + add_index_string(months, i, calendar->month_name_long[i], 1); + add_index_string(smonths, i, calendar->month_name_short[i], 1); + } + add_assoc_zval(*ret, "months", months); + add_assoc_zval(*ret, "abbrevmonths", smonths); + add_assoc_long(*ret, "maxdaysinmonth", calendar->max_days_in_month); + add_assoc_string(*ret, "calname", calendar->name, 1); + add_assoc_string(*ret, "calsymbol", calendar->symbol, 1); + +} + +/* {{{ proto array cal_info([int calendar]) + Returns information about a particular calendar */ +PHP_FUNCTION(cal_info) +{ + long cal = -1; + + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &cal) == FAILURE) { + RETURN_FALSE; + } + + if (cal == -1) { + int i; + zval *val; + + array_init(return_value); + + for (i = 0; i < CAL_NUM_CALS; i++) { + MAKE_STD_ZVAL(val); + _php_cal_info(i, &val); + add_index_zval(return_value, i, val); + } + return; + } + + + if (cal != -1 && (cal < 0 || cal >= CAL_NUM_CALS)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid calendar ID %ld.", cal); + RETURN_FALSE; + } + + _php_cal_info(cal, &return_value); + +} +/* }}} */ + +/* {{{ proto int cal_days_in_month(int calendar, int month, int year) + Returns the number of days in a month for a given year and calendar */ +PHP_FUNCTION(cal_days_in_month) +{ + long cal, month, year; + struct cal_entry_t *calendar; + long sdn_start, sdn_next; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &cal, &month, &year) == FAILURE) { + RETURN_FALSE; + } + + if (cal < 0 || cal >= CAL_NUM_CALS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid calendar ID %ld.", cal); + RETURN_FALSE; + } + + calendar = &cal_conversion_table[cal]; + + sdn_start = calendar->to_jd(year, month, 1); + + if (sdn_start == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid date."); + RETURN_FALSE; + } + + sdn_next = calendar->to_jd(year, 1 + month, 1); + + if (sdn_next == 0) { + /* If the next month is invalid, then we need to try the first month of + * the next year, bearing in mind that the next year after 1 BCE is + * actually 1 AD and not 0. */ + if (year == -1) { + sdn_next = calendar->to_jd(1, 1, 1); + } + else { + sdn_next = calendar->to_jd(year + 1, 1, 1); + } + } + + RETURN_LONG(sdn_next - sdn_start); +} +/* }}} */ + +/* {{{ proto int cal_to_jd(int calendar, int month, int day, int year) + Converts from a supported calendar to Julian Day Count */ +PHP_FUNCTION(cal_to_jd) +{ + long cal, month, day, year; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "llll", &cal, &month, &day, &year) != SUCCESS) { + RETURN_FALSE; + } + + if (cal < 0 || cal >= CAL_NUM_CALS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid calendar ID %ld.", cal); + RETURN_FALSE; + } + + RETURN_LONG(cal_conversion_table[cal].to_jd(year, month, day)); +} +/* }}} */ + +/* {{{ proto array cal_from_jd(int jd, int calendar) + Converts from Julian Day Count to a supported calendar and return extended information */ +PHP_FUNCTION(cal_from_jd) +{ + long jd, cal; + int month, day, year, dow; + char date[16]; + struct cal_entry_t *calendar; + + if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "ll", &jd, &cal) == FAILURE) { + RETURN_FALSE; + } + + if (cal < 0 || cal >= CAL_NUM_CALS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid calendar ID %ld", cal); + RETURN_FALSE; + } + calendar = &cal_conversion_table[cal]; + + array_init(return_value); + + calendar->from_jd(jd, &year, &month, &day); + + snprintf(date, sizeof(date), "%i/%i/%i", month, day, year); + add_assoc_string(return_value, "date", date, 1); + + add_assoc_long(return_value, "month", month); + add_assoc_long(return_value, "day", day); + add_assoc_long(return_value, "year", year); + +/* day of week */ + dow = DayOfWeek(jd); + add_assoc_long(return_value, "dow", dow); + add_assoc_string(return_value, "abbrevdayname", DayNameShort[dow], 1); + add_assoc_string(return_value, "dayname", DayNameLong[dow], 1); +/* month name */ + add_assoc_string(return_value, "abbrevmonth", calendar->month_name_short[month], 1); + add_assoc_string(return_value, "monthname", calendar->month_name_long[month], 1); +} +/* }}} */ + +/* {{{ proto string jdtogregorian(int juliandaycount) + Converts a julian day count to a gregorian calendar date */ +PHP_FUNCTION(jdtogregorian) +{ + long julday; + int year, month, day; + char date[16]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &julday) == FAILURE) { + RETURN_FALSE; + } + + SdnToGregorian(julday, &year, &month, &day); + snprintf(date, sizeof(date), "%i/%i/%i", month, day, year); + + RETURN_STRING(date, 1); +} +/* }}} */ + +/* {{{ proto int gregoriantojd(int month, int day, int year) + Converts a gregorian calendar date to julian day count */ +PHP_FUNCTION(gregoriantojd) +{ + long year, month, day; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &month, &day, &year) == FAILURE) { + RETURN_FALSE; + } + + RETURN_LONG(GregorianToSdn(year, month, day)); +} +/* }}} */ + +/* {{{ proto string jdtojulian(int juliandaycount) + Convert a julian day count to a julian calendar date */ +PHP_FUNCTION(jdtojulian) +{ + long julday; + int year, month, day; + char date[16]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &julday) == FAILURE) { + RETURN_FALSE; + } + + SdnToJulian(julday, &year, &month, &day); + snprintf(date, sizeof(date), "%i/%i/%i", month, day, year); + + RETURN_STRING(date, 1); +} +/* }}} */ + +/* {{{ proto int juliantojd(int month, int day, int year) + Converts a julian calendar date to julian day count */ +PHP_FUNCTION(juliantojd) +{ + long year, month, day; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &month, &day, &year) == FAILURE) { + RETURN_FALSE; + } + + RETURN_LONG(JulianToSdn(year, month, day)); +} +/* }}} */ + +/* {{{ heb_number_to_chars*/ +/* +caution: the Hebrew format produces non unique result. +for example both: year '5' and year '5000' produce 'ה'. +use the numeric one for calculations. + */ +static char *heb_number_to_chars(int n, int fl, char **ret) +{ + char *p, old[18], *endofalafim; + + p = endofalafim = old; +/* + prevents the option breaking the jewish beliefs, and some other + critical resources ;) + */ + if (n > 9999 || n < 1) { + *ret = NULL; + return NULL; + } + +/* alafim (thousands) case */ + if (n / 1000) { + *p = alef_bet[n / 1000]; + p++; + + if (CAL_JEWISH_ADD_ALAFIM_GERESH & fl) { + *p = '\''; + p++; + } + if (CAL_JEWISH_ADD_ALAFIM & fl) { + strcpy(p, " אלפים "); + p += 7; + } + + endofalafim = p; + n = n % 1000; + } + +/* tav-tav (tav=400) case */ + while (n >= 400) { + *p = alef_bet[22]; + p++; + n -= 400; + } + +/* meot (hundreads) case */ + if (n >= 100) { + *p = alef_bet[18 + n / 100]; + p++; + n = n % 100; + } + +/* tet-vav & tet-zain case (special case for 15 and 16) */ + if (n == 15 || n == 16) { + *p = alef_bet[9]; + p++; + *p = alef_bet[n - 9]; + p++; + } else { +/* asarot (tens) case */ + if (n >= 10) { + *p = alef_bet[9 + n / 10]; + p++; + n = n % 10; + } + +/* yehidot (ones) case */ + if (n > 0) { + *p = alef_bet[n]; + p++; + } + } + + if (CAL_JEWISH_ADD_GERESHAYIM & fl) { + switch (p - endofalafim) { + case 0: + break; + case 1: + *p = '\''; + p++; + break; + default: + *(p) = *(p - 1); + *(p - 1) = '"'; + p++; + } + } + + *p = '\0'; + *ret = estrndup(old, (p - old) + 1); + p = *ret; + return p; +} +/* }}} */ + +/* {{{ proto string jdtojewish(int juliandaycount [, bool hebrew [, int fl]]) + Converts a julian day count to a jewish calendar date */ +PHP_FUNCTION(jdtojewish) +{ + long julday, fl = 0; + zend_bool heb = 0; + int year, month, day; + char date[16], hebdate[32]; + char *dayp, *yearp; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|bl", &julday, &heb, &fl) == FAILURE) { + RETURN_FALSE; + } + + SdnToJewish(julday, &year, &month, &day); + if (!heb) { + snprintf(date, sizeof(date), "%i/%i/%i", month, day, year); + RETURN_STRING(date, 1); + } else { + if (year <= 0 || year > 9999) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Year out of range (0-9999)."); + RETURN_FALSE; + } + + snprintf(hebdate, sizeof(hebdate), "%s %s %s", heb_number_to_chars(day, fl, &dayp), JewishMonthHebName[month], heb_number_to_chars(year, fl, &yearp)); + + if (dayp) { + efree(dayp); + } + if (yearp) { + efree(yearp); + } + + RETURN_STRING(hebdate, 1); + + } +} +/* }}} */ + +/* {{{ proto int jewishtojd(int month, int day, int year) + Converts a jewish calendar date to a julian day count */ +PHP_FUNCTION(jewishtojd) +{ + long year, month, day; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &month, &day, &year) == FAILURE) { + RETURN_FALSE; + } + + RETURN_LONG(JewishToSdn(year, month, day)); +} +/* }}} */ + +/* {{{ proto string jdtofrench(int juliandaycount) + Converts a julian day count to a french republic calendar date */ +PHP_FUNCTION(jdtofrench) +{ + long julday; + int year, month, day; + char date[16]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &julday) == FAILURE) { + RETURN_FALSE; + } + + SdnToFrench(julday, &year, &month, &day); + snprintf(date, sizeof(date), "%i/%i/%i", month, day, year); + + RETURN_STRING(date, 1); +} +/* }}} */ + +/* {{{ proto int frenchtojd(int month, int day, int year) + Converts a french republic calendar date to julian day count */ +PHP_FUNCTION(frenchtojd) +{ + long year, month, day; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &month, &day, &year) == FAILURE) { + RETURN_FALSE; + } + + RETURN_LONG(FrenchToSdn(year, month, day)); +} +/* }}} */ + +/* {{{ proto mixed jddayofweek(int juliandaycount [, int mode]) + Returns name or number of day of week from julian day count */ +PHP_FUNCTION(jddayofweek) +{ + long julday, mode = CAL_DOW_DAYNO; + int day; + char *daynamel, *daynames; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &julday, &mode) == FAILURE) { + RETURN_FALSE; + } + + day = DayOfWeek(julday); + daynamel = DayNameLong[day]; + daynames = DayNameShort[day]; + + switch (mode) { + case CAL_DOW_SHORT: + RETURN_STRING(daynamel, 1); + break; + case CAL_DOW_LONG: + RETURN_STRING(daynames, 1); + break; + case CAL_DOW_DAYNO: + default: + RETURN_LONG(day); + break; + } +} +/* }}} */ + +/* {{{ proto string jdmonthname(int juliandaycount, int mode) + Returns name of month for julian day count */ +PHP_FUNCTION(jdmonthname) +{ + long julday, mode; + char *monthname = NULL; + int month, day, year; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &julday, &mode) == FAILURE) { + RETURN_FALSE; + } + + switch (mode) { + case CAL_MONTH_GREGORIAN_LONG: /* gregorian or julian month */ + SdnToGregorian(julday, &year, &month, &day); + monthname = MonthNameLong[month]; + break; + case CAL_MONTH_JULIAN_SHORT: /* gregorian or julian month */ + SdnToJulian(julday, &year, &month, &day); + monthname = MonthNameShort[month]; + break; + case CAL_MONTH_JULIAN_LONG: /* gregorian or julian month */ + SdnToJulian(julday, &year, &month, &day); + monthname = MonthNameLong[month]; + break; + case CAL_MONTH_JEWISH: /* jewish month */ + SdnToJewish(julday, &year, &month, &day); + monthname = JewishMonthName[month]; + break; + case CAL_MONTH_FRENCH: /* french month */ + SdnToFrench(julday, &year, &month, &day); + monthname = FrenchMonthName[month]; + break; + default: /* default gregorian */ + case CAL_MONTH_GREGORIAN_SHORT: /* gregorian or julian month */ + SdnToGregorian(julday, &year, &month, &day); + monthname = MonthNameShort[month]; + break; + } + + RETURN_STRING(monthname, 1); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/calendar/config.m4 b/ext/calendar/config.m4 new file mode 100644 index 0000000..a80101a --- /dev/null +++ b/ext/calendar/config.m4 @@ -0,0 +1,11 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_ENABLE(calendar,whether to enable calendar conversion support, +[ --enable-calendar Enable support for calendar conversion]) + +if test "$PHP_CALENDAR" = "yes"; then + AC_DEFINE(HAVE_CALENDAR,1,[ ]) + PHP_NEW_EXTENSION(calendar, calendar.c dow.c french.c gregor.c jewish.c julian.c easter.c cal_unix.c, $ext_shared) +fi diff --git a/ext/calendar/config.w32 b/ext/calendar/config.w32 new file mode 100644 index 0000000..bd9faba --- /dev/null +++ b/ext/calendar/config.w32 @@ -0,0 +1,10 @@ +// $Id$ +// vim:ft=javascript + +ARG_ENABLE("calendar", "calendar conversion support", "yes"); + +if (PHP_CALENDAR == "yes") { + EXTENSION("calendar", "calendar.c dow.c french.c gregor.c jewish.c \ + julian.c easter.c cal_unix.c"); + AC_DEFINE('HAVE_CALENDAR', 1, 'Have calendar'); +} diff --git a/ext/calendar/dow.c b/ext/calendar/dow.c new file mode 100644 index 0000000..64ae008 --- /dev/null +++ b/ext/calendar/dow.c @@ -0,0 +1,76 @@ + +/* $selId: dow.c,v 2.0 1995/10/24 01:13:06 lees Exp $ + * Copyright 1993-1995, Scott E. Lee, all rights reserved. + * Permission granted to use, copy, modify, distribute and sell so long as + * the above copyright and this permission statement are retained in all + * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. + */ + +/************************************************************************** + * + * These are the externally visible components of this file: + * + * int + * DayOfWeek( + * long int sdn); + * + * Convert a SDN to a day-of-week number (0 to 6). Where 0 stands for + * Sunday, 1 for Monday, etc. and 6 stands for Saturday. + * + * char *DayNameShort[7]; + * + * Convert a day-of-week number (0 to 6), as returned from DayOfWeek(), to + * the abbreviated (three character) name of the day. + * + * char *DayNameLong[7]; + * + * Convert a day-of-week number (0 to 6), as returned from DayOfWeek(), to + * the name of the day. + * + **************************************************************************/ + +#include "sdncal.h" + +int DayOfWeek( + long int sdn) +{ + int dow; + + dow = (sdn + 1) % 7; + if (dow >= 0) { + return (dow); + } else { + return (dow + 7); + } +} + +char *DayNameShort[7] = +{ + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" +}; + +char *DayNameLong[7] = +{ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/calendar/easter.c b/ext/calendar/easter.c new file mode 100644 index 0000000..1948ff9 --- /dev/null +++ b/ext/calendar/easter.c @@ -0,0 +1,145 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Shane Caraveo <shane@caraveo.com> | + | Colin Viebrock <colin@easydns.com> | + | Hartmut Holzgraefe <hholzgra@php.net> | + +----------------------------------------------------------------------+ + */ +/* $Id: */ + +#include "php.h" +#include "php_calendar.h" +#include "sdncal.h" +#include <time.h> + +static void _cal_easter(INTERNAL_FUNCTION_PARAMETERS, int gm) +{ + + /* based on code by Simon Kershaw, <webmaster@ely.anglican.org> */ + + struct tm te; + long year, golden, solar, lunar, pfm, dom, tmp, easter; + long method = CAL_EASTER_DEFAULT; + + /* Default to the current year if year parameter is not given */ + { + time_t a; + struct tm b, *res; + time(&a); + res = php_localtime_r(&a, &b); + if (!res) { + year = 1900; + } else { + year = 1900 + b.tm_year; + } + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "|ll", &year, &method) == FAILURE) { + return; + } + + if (gm && (year<1970 || year>2037)) { /* out of range for timestamps */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function is only valid for years between 1970 and 2037 inclusive"); + RETURN_FALSE; + } + + golden = (year % 19) + 1; /* the Golden number */ + + if ((year <= 1582 && method != CAL_EASTER_ALWAYS_GREGORIAN) || + (year >= 1583 && year <= 1752 && method != CAL_EASTER_ROMAN && method != CAL_EASTER_ALWAYS_GREGORIAN) || + method == CAL_EASTER_ALWAYS_JULIAN) { /* JULIAN CALENDAR */ + + dom = (year + (year/4) + 5) % 7; /* the "Dominical number" - finding a Sunday */ + if (dom < 0) { + dom += 7; + } + + pfm = (3 - (11*golden) - 7) % 30; /* uncorrected date of the Paschal full moon */ + if (pfm < 0) { + pfm += 30; + } + } else { /* GREGORIAN CALENDAR */ + dom = (year + (year/4) - (year/100) + (year/400)) % 7; /* the "Domincal number" */ + if (dom < 0) { + dom += 7; + } + + solar = (year-1600)/100 - (year-1600)/400; /* the solar and lunar corrections */ + lunar = (((year-1400) / 100) * 8) / 25; + + pfm = (3 - (11*golden) + solar - lunar) % 30; /* uncorrected date of the Paschal full moon */ + if (pfm < 0) { + pfm += 30; + } + } + + if ((pfm == 29) || (pfm == 28 && golden > 11)) { /* corrected date of the Paschal full moon */ + pfm--; /* - days after 21st March */ + } + + tmp = (4-pfm-dom) % 7; + if (tmp < 0) { + tmp += 7; + } + + easter = pfm + tmp + 1; /* Easter as the number of days after 21st March */ + + if (gm) { /* return a timestamp */ + te.tm_isdst = -1; + te.tm_year = year-1900; + te.tm_sec = 0; + te.tm_min = 0; + te.tm_hour = 0; + + if (easter < 11) { + te.tm_mon = 2; /* March */ + te.tm_mday = easter+21; + } else { + te.tm_mon = 3; /* April */ + te.tm_mday = easter-10; + } + + Z_LVAL_P(return_value) = mktime(&te); + } else { /* return the days after March 21 */ + Z_LVAL_P(return_value) = easter; + } + + Z_TYPE_P(return_value) = IS_LONG; + +} + +/* {{{ proto int easter_date([int year]) + Return the timestamp of midnight on Easter of a given year (defaults to current year) */ +PHP_FUNCTION(easter_date) +{ + _cal_easter(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto int easter_days([int year, [int method]]) + Return the number of days after March 21 that Easter falls on for a given year (defaults to current year) */ +PHP_FUNCTION(easter_days) +{ + _cal_easter(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/calendar/french.c b/ext/calendar/french.c new file mode 100644 index 0000000..5b4dd53 --- /dev/null +++ b/ext/calendar/french.c @@ -0,0 +1,160 @@ +/* $selId: french.c,v 2.0 1995/10/24 01:13:06 lees Exp $ + * Copyright 1993-1995, Scott E. Lee, all rights reserved. + * Permission granted to use, copy, modify, distribute and sell so long as + * the above copyright and this permission statement are retained in all + * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. + */ + +/************************************************************************** + * + * These are the externally visible components of this file: + * + * void + * SdnToFrench( + * long int sdn, + * int *pYear, + * int *pMonth, + * int *pDay); + * + * Convert a SDN to a French republican calendar date. If the input SDN is + * before the first day of year 1 or after the last day of year 14, the + * three output values will all be set to zero, otherwise *pYear will be in + * the range 1 to 14 inclusive; *pMonth will be in the range 1 to 13 + * inclusive; *pDay will be in the range 1 to 30 inclusive. If *pMonth is + * 13, the SDN represents one of the holidays at the end of the year and + * *pDay will be in the range 1 to 6 inclusive. + * + * long int + * FrenchToSdn( + * int year, + * int month, + * int day); + * + * Convert a French republican calendar date to a SDN. Zero is returned + * when the input date is detected as invalid or out of the supported + * range. The return value will be > 0 for all valid, supported dates, but + * there are some invalid dates that will return a positive value. To + * verify that a date is valid, convert it to SDN and then back and compare + * with the original. + * + * char *FrenchMonthName[14]; + * + * Convert a French republican month number (1 to 13) to the name of the + * French republican month (null terminated). An index of 13 (for the + * "extra" days at the end of the year) will return the string "Extra". An + * index of zero will return a zero length string. + * + * VALID RANGE + * + * These routines only convert dates in years 1 through 14 (Gregorian + * dates 22 September 1792 through 22 September 1806). This more than + * covers the period when the calendar was in use. + * + * I would support a wider range of dates, but I have not been able to + * find an authoritative definition of when leap years were to have + * occurred. There are suggestions that it was to skip a leap year ever + * 100 years like the Gregorian calendar. + * + * CALENDAR OVERVIEW + * + * The French republican calendar was adopted in October 1793 during + * the French Revolution and was abandoned in January 1806. The intent + * was to create a new calendar system that was based on scientific + * principals, not religious traditions. + * + * The year is divided into 12 months of 30 days each. The remaining 5 + * to 6 days in the year are grouped at the end and are holidays. Each + * month is divided into three decades (instead of weeks) of 10 days + * each. + * + * The epoch (first day of the first year) is 22 September 1792 in the + * Gregorian calendar. Leap years are every fourth year (year 3, 7, + * 11, etc.) + * + * TESTING + * + * This algorithm has been tested from the year 1 to 14. The source + * code of the verification program is included in this package. + * + * REFERENCES + * + * I have found no detailed, authoritative reference on this calendar. + * The algorithms are based on a preponderance of less authoritative + * sources. + * + **************************************************************************/ + +#include "sdncal.h" + +#define FRENCH_SDN_OFFSET 2375474 +#define DAYS_PER_4_YEARS 1461 +#define DAYS_PER_MONTH 30 +#define FIRST_VALID 2375840 +#define LAST_VALID 2380952 + +void SdnToFrench( + long int sdn, + int *pYear, + int *pMonth, + int *pDay) +{ + long int temp; + int dayOfYear; + + if (sdn < FIRST_VALID || sdn > LAST_VALID) { + *pYear = 0; + *pMonth = 0; + *pDay = 0; + return; + } + temp = (sdn - FRENCH_SDN_OFFSET) * 4 - 1; + *pYear = temp / DAYS_PER_4_YEARS; + dayOfYear = (temp % DAYS_PER_4_YEARS) / 4; + *pMonth = dayOfYear / DAYS_PER_MONTH + 1; + *pDay = dayOfYear % DAYS_PER_MONTH + 1; +} + +long int FrenchToSdn( + int year, + int month, + int day) +{ + /* check for invalid dates */ + if (year < 1 || year > 14 || + month < 1 || month > 13 || + day < 1 || day > 30) { + return (0); + } + return ((year * DAYS_PER_4_YEARS) / 4 + + (month - 1) * DAYS_PER_MONTH + + day + + FRENCH_SDN_OFFSET); +} + +char *FrenchMonthName[14] = +{ + "", + "Vendemiaire", + "Brumaire", + "Frimaire", + "Nivose", + "Pluviose", + "Ventose", + "Germinal", + "Floreal", + "Prairial", + "Messidor", + "Thermidor", + "Fructidor", + "Extra" +}; + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/calendar/gregor.c b/ext/calendar/gregor.c new file mode 100644 index 0000000..ebb743f --- /dev/null +++ b/ext/calendar/gregor.c @@ -0,0 +1,270 @@ +/* $selId: gregor.c,v 2.0 1995/10/24 01:13:06 lees Exp $ + * Copyright 1993-1995, Scott E. Lee, all rights reserved. + * Permission granted to use, copy, modify, distribute and sell so long as + * the above copyright and this permission statement are retained in all + * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. + */ + +/************************************************************************** + * + * These are the externally visible components of this file: + * + * void + * SdnToGregorian( + * long int sdn, + * int *pYear, + * int *pMonth, + * int *pDay); + * + * Convert a SDN to a Gregorian calendar date. If the input SDN is less + * than 1, the three output values will all be set to zero, otherwise + * *pYear will be >= -4714 and != 0; *pMonth will be in the range 1 to 12 + * inclusive; *pDay will be in the range 1 to 31 inclusive. + * + * long int + * GregorianToSdn( + * int inputYear, + * int inputMonth, + * int inputDay); + * + * Convert a Gregorian calendar date to a SDN. Zero is returned when the + * input date is detected as invalid or out of the supported range. The + * return value will be > 0 for all valid, supported dates, but there are + * some invalid dates that will return a positive value. To verify that a + * date is valid, convert it to SDN and then back and compare with the + * original. + * + * char *MonthNameShort[13]; + * + * Convert a Gregorian month number (1 to 12) to the abbreviated (three + * character) name of the Gregorian month (null terminated). An index of + * zero will return a zero length string. + * + * char *MonthNameLong[13]; + * + * Convert a Gregorian month number (1 to 12) to the name of the Gregorian + * month (null terminated). An index of zero will return a zero length + * string. + * + * VALID RANGE + * + * 4714 B.C. to at least 10000 A.D. + * + * Although this software can handle dates all the way back to 4714 + * B.C., such use may not be meaningful. The Gregorian calendar was + * not instituted until October 15, 1582 (or October 5, 1582 in the + * Julian calendar). Some countries did not accept it until much + * later. For example, Britain converted in 1752, The USSR in 1918 and + * Greece in 1923. Most European countries used the Julian calendar + * prior to the Gregorian. + * + * CALENDAR OVERVIEW + * + * The Gregorian calendar is a modified version of the Julian calendar. + * The only difference being the specification of leap years. The + * Julian calendar specifies that every year that is a multiple of 4 + * will be a leap year. This leads to a year that is 365.25 days long, + * but the current accepted value for the tropical year is 365.242199 + * days. + * + * To correct this error in the length of the year and to bring the + * vernal equinox back to March 21, Pope Gregory XIII issued a papal + * bull declaring that Thursday October 4, 1582 would be followed by + * Friday October 15, 1582 and that centennial years would only be a + * leap year if they were a multiple of 400. This shortened the year + * by 3 days per 400 years, giving a year of 365.2425 days. + * + * Another recently proposed change in the leap year rule is to make + * years that are multiples of 4000 not a leap year, but this has never + * been officially accepted and this rule is not implemented in these + * algorithms. + * + * ALGORITHMS + * + * The calculations are based on three different cycles: a 400 year + * cycle of leap years, a 4 year cycle of leap years and a 5 month + * cycle of month lengths. + * + * The 5 month cycle is used to account for the varying lengths of + * months. You will notice that the lengths alternate between 30 + * and 31 days, except for three anomalies: both July and August + * have 31 days, both December and January have 31, and February + * is less than 30. Starting with March, the lengths are in a + * cycle of 5 months (31, 30, 31, 30, 31): + * + * Mar 31 days \ + * Apr 30 days | + * May 31 days > First cycle + * Jun 30 days | + * Jul 31 days / + * + * Aug 31 days \ + * Sep 30 days | + * Oct 31 days > Second cycle + * Nov 30 days | + * Dec 31 days / + * + * Jan 31 days \ + * Feb 28/9 days | + * > Third cycle (incomplete) + * + * For this reason the calculations (internally) assume that the + * year starts with March 1. + * + * TESTING + * + * This algorithm has been tested from the year 4714 B.C. to 10000 + * A.D. The source code of the verification program is included in + * this package. + * + * REFERENCES + * + * Conversions Between Calendar Date and Julian Day Number by Robert J. + * Tantzen, Communications of the Association for Computing Machinery + * August 1963. (Also published in Collected Algorithms from CACM, + * algorithm number 199). + * + **************************************************************************/ + +#include "sdncal.h" +#include <limits.h> + +#define GREGOR_SDN_OFFSET 32045 +#define DAYS_PER_5_MONTHS 153 +#define DAYS_PER_4_YEARS 1461 +#define DAYS_PER_400_YEARS 146097 + +void SdnToGregorian( + long int sdn, + int *pYear, + int *pMonth, + int *pDay) +{ + int century; + int year; + int month; + int day; + long int temp; + int dayOfYear; + + if (sdn <= 0 || + sdn > (LONG_MAX - 4 * GREGOR_SDN_OFFSET) / 4) { + goto fail; + } + temp = (sdn + GREGOR_SDN_OFFSET) * 4 - 1; + + /* Calculate the century (year/100). */ + century = temp / DAYS_PER_400_YEARS; + + /* Calculate the year and day of year (1 <= dayOfYear <= 366). */ + temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3; + year = (century * 100) + (temp / DAYS_PER_4_YEARS); + dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1; + + /* Calculate the month and day of month. */ + temp = dayOfYear * 5 - 3; + month = temp / DAYS_PER_5_MONTHS; + day = (temp % DAYS_PER_5_MONTHS) / 5 + 1; + + /* Convert to the normal beginning of the year. */ + if (month < 10) { + month += 3; + } else { + year += 1; + month -= 9; + } + + /* Adjust to the B.C./A.D. type numbering. */ + year -= 4800; + if (year <= 0) + year--; + + *pYear = year; + *pMonth = month; + *pDay = day; + return; + +fail: + *pYear = 0; + *pMonth = 0; + *pDay = 0; +} + +long int GregorianToSdn( + int inputYear, + int inputMonth, + int inputDay) +{ + int year; + int month; + + /* check for invalid dates */ + if (inputYear == 0 || inputYear < -4714 || + inputMonth <= 0 || inputMonth > 12 || + inputDay <= 0 || inputDay > 31) { + return (0); + } + /* check for dates before SDN 1 (Nov 25, 4714 B.C.) */ + if (inputYear == -4714) { + if (inputMonth < 11) { + return (0); + } + if (inputMonth == 11 && inputDay < 25) { + return (0); + } + } + /* Make year always a positive number. */ + if (inputYear < 0) { + year = inputYear + 4801; + } else { + year = inputYear + 4800; + } + + /* Adjust the start of the year. */ + if (inputMonth > 2) { + month = inputMonth - 3; + } else { + month = inputMonth + 9; + year--; + } + + return (((year / 100) * DAYS_PER_400_YEARS) / 4 + + ((year % 100) * DAYS_PER_4_YEARS) / 4 + + (month * DAYS_PER_5_MONTHS + 2) / 5 + + inputDay + - GREGOR_SDN_OFFSET); +} + +char *MonthNameShort[13] = +{ + "", + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +}; + +char *MonthNameLong[13] = +{ + "", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +}; diff --git a/ext/calendar/jewish.c b/ext/calendar/jewish.c new file mode 100644 index 0000000..f4dc7c3 --- /dev/null +++ b/ext/calendar/jewish.c @@ -0,0 +1,763 @@ +/* $selId: jewish.c,v 2.0 1995/10/24 01:13:06 lees Exp $ + * Copyright 1993-1995, Scott E. Lee, all rights reserved. + * Permission granted to use, copy, modify, distribute and sell so long as + * the above copyright and this permission statement are retained in all + * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. + */ + +/************************************************************************** + * + * These are the externally visible components of this file: + * + * void + * SdnToJewish( + * long int sdn, + * int *pYear, + * int *pMonth, + * int *pDay); + * + * Convert a SDN to a Jewish calendar date. If the input SDN is before the + * first day of year 1, the three output values will all be set to zero, + * otherwise *pYear will be > 0; *pMonth will be in the range 1 to 13 + * inclusive; *pDay will be in the range 1 to 30 inclusive. Note that Adar + * II is assigned the month number 7 and Elul is always 13. + * + * long int + * JewishToSdn( + * int year, + * int month, + * int day); + * + * Convert a Jewish calendar date to a SDN. Zero is returned when the + * input date is detected as invalid or out of the supported range. The + * return value will be > 0 for all valid, supported dates, but there are + * some invalid dates that will return a positive value. To verify that a + * date is valid, convert it to SDN and then back and compare with the + * original. + * + * char *JewishMonthName[14]; + * + * Convert a Jewish month number (1 to 13) to the name of the Jewish month + * (null terminated). An index of zero will return a zero length string. + * + * VALID RANGE + * + * Although this software can handle dates all the way back to the year + * 1 (3761 B.C.), such use may not be meaningful. + * + * The Jewish calendar has been in use for several thousand years, but + * in the early days there was no formula to determine the start of a + * month. A new month was started when the new moon was first + * observed. + * + * It is not clear when the current rule based calendar replaced the + * observation based calendar. According to the book "Jewish Calendar + * Mystery Dispelled" by George Zinberg, the patriarch Hillel II + * published these rules in 358 A.D. But, according to The + * Encyclopedia Judaica, Hillel II may have only published the 19 year + * rule for determining the occurrence of leap years. + * + * I have yet to find a specific date when the current set of rules + * were known to be in use. + * + * CALENDAR OVERVIEW + * + * The Jewish calendar is based on lunar as well as solar cycles. A + * month always starts on or near a new moon and has either 29 or 30 + * days (a lunar cycle is about 29 1/2 days). Twelve of these + * alternating 29-30 day months gives a year of 354 days, which is + * about 11 1/4 days short of a solar year. + * + * Since a month is defined to be a lunar cycle (new moon to new moon), + * this 11 1/4 day difference cannot be overcome by adding days to a + * month as with the Gregorian calendar, so an entire month is + * periodically added to the year, making some years 13 months long. + * + * For astronomical as well as ceremonial reasons, the start of a new + * year may be delayed until a day or two after the new moon causing + * years to vary in length. Leap years can be from 383 to 385 days and + * common years can be from 353 to 355 days. These are the months of + * the year and their possible lengths: + * + * COMMON YEAR LEAP YEAR + * 1 Tishri 30 30 30 30 30 30 + * 2 Heshvan 29 29 30 29 29 30 (variable) + * 3 Kislev 29 30 30 29 30 30 (variable) + * 4 Tevet 29 29 29 29 29 29 + * 5 Shevat 30 30 30 30 30 30 + * 6 Adar I 29 29 29 30 30 30 (variable) + * 7 Adar II -- -- -- 29 29 29 (optional) + * 8 Nisan 30 30 30 30 30 30 + * 9 Iyyar 29 29 29 29 29 29 + * 10 Sivan 30 30 30 30 30 30 + * 11 Tammuz 29 29 29 29 29 29 + * 12 Av 30 30 30 30 30 30 + * 13 Elul 29 29 29 29 29 29 + * --- --- --- --- --- --- + * 353 354 355 383 384 385 + * + * Note that the month names and other words that appear in this file + * have multiple possible spellings in the Roman character set. I have + * chosen to use the spellings found in the Encyclopedia Judaica. + * + * Adar II, the month added for leap years, is sometimes referred to as + * the 13th month, but I have chosen to assign it the number 7 to keep + * the months in chronological order. This may not be consistent with + * other numbering schemes. + * + * Leap years occur in a fixed pattern of 19 years called the metonic + * cycle. The 3rd, 6th, 8th, 11th, 14th, 17th and 19th years of this + * cycle are leap years. The first metonic cycle starts with Jewish + * year 1, or 3761/60 B.C. This is believed to be the year of + * creation. + * + * To construct the calendar for a year, you must first find the length + * of the year by determining the first day of the year (Tishri 1, or + * Rosh Ha-Shanah) and the first day of the following year. This + * selects one of the six possible month length configurations listed + * above. + * + * Finding the first day of the year is the most difficult part. + * Finding the date and time of the new moon (or molad) is the first + * step. For this purpose, the lunar cycle is assumed to be 29 days 12 + * hours and 793 halakim. A halakim is 1/1080th of an hour or 3 1/3 + * seconds. (This assumed value is only about 1/2 second less than the + * value used by modern astronomers -- not bad for a number that was + * determined so long ago.) The first molad of year 1 occurred on + * Sunday at 11:20:11 P.M. This would actually be Monday, because the + * Jewish day is considered to begin at sunset. + * + * Since sunset varies, the day is assumed to begin at 6:00 P.M. for + * calendar calculation purposes. So, the first molad was 5 hours 793 + * halakim after the start of Tishri 1, 0001 (which was Monday + * September 7, 4761 B.C. by the Gregorian calendar). All subsequent + * molads can be calculated from this starting point by adding the + * length of a lunar cycle. + * + * Once the molad that starts a year is determined the actual start of + * the year (Tishri 1) can be determined. Tishri 1 will be the day of + * the molad unless it is delayed by one of the following four rules + * (called dehiyyot). Each rule can delay the start of the year by one + * day, and since rule #1 can combine with one of the other rules, it + * can be delayed as much as two days. + * + * 1. Tishri 1 must never be Sunday, Wednesday or Friday. (This + * is largely to prevent certain holidays from occurring on the + * day before or after the Sabbath.) + * + * 2. If the molad occurs on or after noon, Tishri 1 must be + * delayed. + * + * 3. If it is a common (not leap) year and the molad occurs on + * Tuesday at or after 3:11:20 A.M., Tishri 1 must be delayed. + * + * 4. If it is the year following a leap year and the molad occurs + * on Monday at or after 9:32:43 and 1/3 sec, Tishri 1 must be + * delayed. + * + * GLOSSARY + * + * dehiyyot The set of 4 rules that determine when the new year + * starts relative to the molad. + * + * halakim 1/1080th of an hour or 3 1/3 seconds. + * + * lunar cycle The period of time between mean conjunctions of the + * sun and moon (new moon to new moon). This is + * assumed to be 29 days 12 hours and 793 halakim for + * calendar purposes. + * + * metonic cycle A 19 year cycle which determines which years are + * leap years and which are common years. The 3rd, + * 6th, 8th, 11th, 14th, 17th and 19th years of this + * cycle are leap years. + * + * molad The date and time of the mean conjunction of the + * sun and moon (new moon). This is the approximate + * beginning of a month. + * + * Rosh Ha-Shanah The first day of the Jewish year (Tishri 1). + * + * Tishri The first month of the Jewish year. + * + * ALGORITHMS + * + * SERIAL DAY NUMBER TO JEWISH DATE + * + * The simplest approach would be to use the rules stated above to find + * the molad of Tishri before and after the given day number. Then use + * the molads to find Tishri 1 of the current and following years. + * From this the length of the year can be determined and thus the + * length of each month. But this method is used as a last resort. + * + * The first 59 days of the year are the same regardless of the length + * of the year. As a result, only the day number of the start of the + * year is required. + * + * Similarly, the last 6 months do not change from year to year. And + * since it can be determined whether the year is a leap year by simple + * division, the lengths of Adar I and II can be easily calculated. In + * fact, all dates after the 3rd month are consistent from year to year + * (once it is known whether it is a leap year). + * + * This means that if the given day number falls in the 3rd month or on + * the 30th day of the 2nd month the length of the year must be found, + * but in no other case. + * + * So, the approach used is to take the given day number and round it + * to the closest molad of Tishri (first new moon of the year). The + * rounding is not really to the *closest* molad, but is such that if + * the day number is before the middle of the 3rd month the molad at + * the start of the year is found, otherwise the molad at the end of + * the year is found. + * + * Only if the day number is actually found to be in the ambiguous + * period of 29 to 31 days is the other molad calculated. + * + * JEWISH DATE TO SERIAL DAY NUMBER + * + * The year number is used to find which 19 year metonic cycle contains + * the date and which year within the cycle (this is a division and + * modulus). This also determines whether it is a leap year. + * + * If the month is 1 or 2, the calculation is simple addition to the + * first of the year. + * + * If the month is 8 (Nisan) or greater, the calculation is simple + * subtraction from beginning of the following year. + * + * If the month is 4 to 7, it is considered whether it is a leap year + * and then simple subtraction from the beginning of the following year + * is used. + * + * Only if it is the 3rd month is both the start and end of the year + * required. + * + * TESTING + * + * This algorithm has been tested in two ways. First, 510 dates from a + * table in "Jewish Calendar Mystery Dispelled" were calculated and + * compared to the table. Second, the calculation algorithm described + * in "Jewish Calendar Mystery Dispelled" was coded and used to verify + * all dates from the year 1 (3761 B.C.) to the year 13760 (10000 + * A.D.). + * + * The source code of the verification program is included in this + * package. + * + * REFERENCES + * + * The Encyclopedia Judaica, the entry for "Calendar" + * + * The Jewish Encyclopedia + * + * Jewish Calendar Mystery Dispelled by George Zinberg, Vantage Press, + * 1963 + * + * The Comprehensive Hebrew Calendar by Arthur Spier, Behrman House + * + * The Book of Calendars [note that this work contains many typos] + * + **************************************************************************/ + +#if defined(PHP_WIN32) && _MSC_VER >= 1200 +#pragma setlocale("english") +#endif + +#include "sdncal.h" + +#define HALAKIM_PER_HOUR 1080 +#define HALAKIM_PER_DAY 25920 +#define HALAKIM_PER_LUNAR_CYCLE ((29 * HALAKIM_PER_DAY) + 13753) +#define HALAKIM_PER_METONIC_CYCLE (HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7)) + +#define JEWISH_SDN_OFFSET 347997 +#define NEW_MOON_OF_CREATION 31524 + +#define SUNDAY 0 +#define MONDAY 1 +#define TUESDAY 2 +#define WEDNESDAY 3 +#define THURSDAY 4 +#define FRIDAY 5 +#define SATURDAY 6 + +#define NOON (18 * HALAKIM_PER_HOUR) +#define AM3_11_20 ((9 * HALAKIM_PER_HOUR) + 204) +#define AM9_32_43 ((15 * HALAKIM_PER_HOUR) + 589) + +static int monthsPerYear[19] = +{ +12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13 +}; + +static int yearOffset[19] = +{ + 0, 12, 24, 37, 49, 61, 74, 86, 99, 111, 123, + 136, 148, 160, 173, 185, 197, 210, 222 +}; + +char *JewishMonthName[14] = +{ + "", + "Tishri", + "Heshvan", + "Kislev", + "Tevet", + "Shevat", + "AdarI", + "AdarII", + "Nisan", + "Iyyar", + "Sivan", + "Tammuz", + "Av", + "Elul" +}; + +char *JewishMonthHebName[14] = +{ + "", + "תשרי", + "חשון", + "כסלו", + "טבת", + "שבט", + "אדר", + "'אדר ב", + "ניסן", + "אייר", + "סיון", + "תמוז", + "אב", + "אלול" +}; + +/************************************************************************ + * Given the year within the 19 year metonic cycle and the time of a molad + * (new moon) which starts that year, this routine will calculate what day + * will be the actual start of the year (Tishri 1 or Rosh Ha-Shanah). This + * first day of the year will be the day of the molad unless one of 4 rules + * (called dehiyyot) delays it. These 4 rules can delay the start of the + * year by as much as 2 days. + */ +static long int Tishri1( + int metonicYear, + long int moladDay, + long int moladHalakim) +{ + long int tishri1; + int dow; + int leapYear; + int lastWasLeapYear; + + tishri1 = moladDay; + dow = tishri1 % 7; + leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 + || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 + || metonicYear == 18; + lastWasLeapYear = metonicYear == 3 || metonicYear == 6 + || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 + || metonicYear == 17 || metonicYear == 0; + + /* Apply rules 2, 3 and 4. */ + if ((moladHalakim >= NOON) || + ((!leapYear) && dow == TUESDAY && moladHalakim >= AM3_11_20) || + (lastWasLeapYear && dow == MONDAY && moladHalakim >= AM9_32_43)) { + tishri1++; + dow++; + if (dow == 7) { + dow = 0; + } + } + /* Apply rule 1 after the others because it can cause an additional + * delay of one day. */ + if (dow == WEDNESDAY || dow == FRIDAY || dow == SUNDAY) { + tishri1++; + } + return (tishri1); +} + +/************************************************************************ + * Given a metonic cycle number, calculate the date and time of the molad + * (new moon) that starts that cycle. Since the length of a metonic cycle + * is a constant, this is a simple calculation, except that it requires an + * intermediate value which is bigger that 32 bits. Because this + * intermediate value only needs 36 to 37 bits and the other numbers are + * constants, the process has been reduced to just a few steps. + */ +static void MoladOfMetonicCycle( + int metonicCycle, + long int *pMoladDay, + long int *pMoladHalakim) +{ + register unsigned long int r1, r2, d1, d2; + + /* Start with the time of the first molad after creation. */ + r1 = NEW_MOON_OF_CREATION; + + /* Calculate metonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32 + * bits of the result will be in r2 and the lower 16 bits will be + * in r1. */ + r1 += metonicCycle * (HALAKIM_PER_METONIC_CYCLE & 0xFFFF); + r2 = r1 >> 16; + r2 += metonicCycle * ((HALAKIM_PER_METONIC_CYCLE >> 16) & 0xFFFF); + + /* Calculate r2r1 / HALAKIM_PER_DAY. The remainder will be in r1, the + * upper 16 bits of the quotient will be in d2 and the lower 16 bits + * will be in d1. */ + d2 = r2 / HALAKIM_PER_DAY; + r2 -= d2 * HALAKIM_PER_DAY; + r1 = (r2 << 16) | (r1 & 0xFFFF); + d1 = r1 / HALAKIM_PER_DAY; + r1 -= d1 * HALAKIM_PER_DAY; + + *pMoladDay = (d2 << 16) | d1; + *pMoladHalakim = r1; +} + +/************************************************************************ + * Given a day number, find the molad of Tishri (the new moon at the start + * of a year) which is closest to that day number. It's not really the + * *closest* molad that we want here. If the input day is in the first two + * months, we want the molad at the start of the year. If the input day is + * in the fourth to last months, we want the molad at the end of the year. + * If the input day is in the third month, it doesn't matter which molad is + * returned, because both will be required. This type of "rounding" allows + * us to avoid calculating the length of the year in most cases. + */ +static void FindTishriMolad( + long int inputDay, + int *pMetonicCycle, + int *pMetonicYear, + long int *pMoladDay, + long int *pMoladHalakim) +{ + long int moladDay; + long int moladHalakim; + int metonicCycle; + int metonicYear; + + /* Estimate the metonic cycle number. Note that this may be an under + * estimate because there are 6939.6896 days in a metonic cycle not + * 6940, but it will never be an over estimate. The loop below will + * correct for any error in this estimate. */ + metonicCycle = (inputDay + 310) / 6940; + + /* Calculate the time of the starting molad for this metonic cycle. */ + MoladOfMetonicCycle(metonicCycle, &moladDay, &moladHalakim); + + /* If the above was an under estimate, increment the cycle number until + * the correct one is found. For modern dates this loop is about 98.6% + * likely to not execute, even once, because the above estimate is + * really quite close. */ + while (moladDay < inputDay - 6940 + 310) { + metonicCycle++; + moladHalakim += HALAKIM_PER_METONIC_CYCLE; + moladDay += moladHalakim / HALAKIM_PER_DAY; + moladHalakim = moladHalakim % HALAKIM_PER_DAY; + } + + /* Find the molad of Tishri closest to this date. */ + for (metonicYear = 0; metonicYear < 18; metonicYear++) { + if (moladDay > inputDay - 74) { + break; + } + moladHalakim += HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear]; + moladDay += moladHalakim / HALAKIM_PER_DAY; + moladHalakim = moladHalakim % HALAKIM_PER_DAY; + } + + *pMetonicCycle = metonicCycle; + *pMetonicYear = metonicYear; + *pMoladDay = moladDay; + *pMoladHalakim = moladHalakim; +} + +/************************************************************************ + * Given a year, find the number of the first day of that year and the date + * and time of the starting molad. + */ +static void FindStartOfYear( + int year, + int *pMetonicCycle, + int *pMetonicYear, + long int *pMoladDay, + long int *pMoladHalakim, + int *pTishri1) +{ + *pMetonicCycle = (year - 1) / 19; + *pMetonicYear = (year - 1) % 19; + MoladOfMetonicCycle(*pMetonicCycle, pMoladDay, pMoladHalakim); + + *pMoladHalakim += HALAKIM_PER_LUNAR_CYCLE * yearOffset[*pMetonicYear]; + *pMoladDay += *pMoladHalakim / HALAKIM_PER_DAY; + *pMoladHalakim = *pMoladHalakim % HALAKIM_PER_DAY; + + *pTishri1 = Tishri1(*pMetonicYear, *pMoladDay, *pMoladHalakim); +} + +/************************************************************************ + * Given a serial day number (SDN), find the corresponding year, month and + * day in the Jewish calendar. The three output values will always be + * modified. If the input SDN is before the first day of year 1, they will + * all be set to zero, otherwise *pYear will be > 0; *pMonth will be in the + * range 1 to 13 inclusive; *pDay will be in the range 1 to 30 inclusive. + */ +void SdnToJewish( + long int sdn, + int *pYear, + int *pMonth, + int *pDay) +{ + long int inputDay; + long int day; + long int halakim; + int metonicCycle; + int metonicYear; + int tishri1; + int tishri1After; + int yearLength; + + if (sdn <= JEWISH_SDN_OFFSET) { + *pYear = 0; + *pMonth = 0; + *pDay = 0; + return; + } + inputDay = sdn - JEWISH_SDN_OFFSET; + + FindTishriMolad(inputDay, &metonicCycle, &metonicYear, &day, &halakim); + tishri1 = Tishri1(metonicYear, day, halakim); + + if (inputDay >= tishri1) { + /* It found Tishri 1 at the start of the year. */ + *pYear = metonicCycle * 19 + metonicYear + 1; + if (inputDay < tishri1 + 59) { + if (inputDay < tishri1 + 30) { + *pMonth = 1; + *pDay = inputDay - tishri1 + 1; + } else { + *pMonth = 2; + *pDay = inputDay - tishri1 - 29; + } + return; + } + /* We need the length of the year to figure this out, so find + * Tishri 1 of the next year. */ + halakim += HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear]; + day += halakim / HALAKIM_PER_DAY; + halakim = halakim % HALAKIM_PER_DAY; + tishri1After = Tishri1((metonicYear + 1) % 19, day, halakim); + } else { + /* It found Tishri 1 at the end of the year. */ + *pYear = metonicCycle * 19 + metonicYear; + if (inputDay >= tishri1 - 177) { + /* It is one of the last 6 months of the year. */ + if (inputDay > tishri1 - 30) { + *pMonth = 13; + *pDay = inputDay - tishri1 + 30; + } else if (inputDay > tishri1 - 60) { + *pMonth = 12; + *pDay = inputDay - tishri1 + 60; + } else if (inputDay > tishri1 - 89) { + *pMonth = 11; + *pDay = inputDay - tishri1 + 89; + } else if (inputDay > tishri1 - 119) { + *pMonth = 10; + *pDay = inputDay - tishri1 + 119; + } else if (inputDay > tishri1 - 148) { + *pMonth = 9; + *pDay = inputDay - tishri1 + 148; + } else { + *pMonth = 8; + *pDay = inputDay - tishri1 + 178; + } + return; + } else { + if (monthsPerYear[(*pYear - 1) % 19] == 13) { + *pMonth = 7; + *pDay = inputDay - tishri1 + 207; + if (*pDay > 0) + return; + (*pMonth)--; + (*pDay) += 30; + if (*pDay > 0) + return; + (*pMonth)--; + (*pDay) += 30; + } else { + *pMonth = 6; + *pDay = inputDay - tishri1 + 207; + if (*pDay > 0) + return; + (*pMonth)--; + (*pDay) += 30; + } + if (*pDay > 0) + return; + (*pMonth)--; + (*pDay) += 29; + if (*pDay > 0) + return; + + /* We need the length of the year to figure this out, so find + * Tishri 1 of this year. */ + tishri1After = tishri1; + FindTishriMolad(day - 365, + &metonicCycle, &metonicYear, &day, &halakim); + tishri1 = Tishri1(metonicYear, day, halakim); + } + } + + yearLength = tishri1After - tishri1; + day = inputDay - tishri1 - 29; + if (yearLength == 355 || yearLength == 385) { + /* Heshvan has 30 days */ + if (day <= 30) { + *pMonth = 2; + *pDay = day; + return; + } + day -= 30; + } else { + /* Heshvan has 29 days */ + if (day <= 29) { + *pMonth = 2; + *pDay = day; + return; + } + day -= 29; + } + + /* It has to be Kislev. */ + *pMonth = 3; + *pDay = day; +} + +/************************************************************************ + * Given a year, month and day in the Jewish calendar, find the + * corresponding serial day number (SDN). Zero is returned when the input + * date is detected as invalid. The return value will be > 0 for all valid + * dates, but there are some invalid dates that will return a positive + * value. To verify that a date is valid, convert it to SDN and then back + * and compare with the original. + */ +long int JewishToSdn( + int year, + int month, + int day) +{ + long int sdn; + int metonicCycle; + int metonicYear; + int tishri1; + int tishri1After; + long int moladDay; + long int moladHalakim; + int yearLength; + int lengthOfAdarIAndII; + + if (year <= 0 || day <= 0 || day > 30) { + return (0); + } + switch (month) { + case 1: + case 2: + /* It is Tishri or Heshvan - don't need the year length. */ + FindStartOfYear(year, &metonicCycle, &metonicYear, + &moladDay, &moladHalakim, &tishri1); + if (month == 1) { + sdn = tishri1 + day - 1; + } else { + sdn = tishri1 + day + 29; + } + break; + + case 3: + /* It is Kislev - must find the year length. */ + + /* Find the start of the year. */ + FindStartOfYear(year, &metonicCycle, &metonicYear, + &moladDay, &moladHalakim, &tishri1); + + /* Find the end of the year. */ + moladHalakim += HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear]; + moladDay += moladHalakim / HALAKIM_PER_DAY; + moladHalakim = moladHalakim % HALAKIM_PER_DAY; + tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim); + + yearLength = tishri1After - tishri1; + + if (yearLength == 355 || yearLength == 385) { + sdn = tishri1 + day + 59; + } else { + sdn = tishri1 + day + 58; + } + break; + + case 4: + case 5: + case 6: + /* It is Tevet, Shevat or Adar I - don't need the year length. */ + + FindStartOfYear(year + 1, &metonicCycle, &metonicYear, + &moladDay, &moladHalakim, &tishri1After); + + if (monthsPerYear[(year - 1) % 19] == 12) { + lengthOfAdarIAndII = 29; + } else { + lengthOfAdarIAndII = 59; + } + + if (month == 4) { + sdn = tishri1After + day - lengthOfAdarIAndII - 237; + } else if (month == 5) { + sdn = tishri1After + day - lengthOfAdarIAndII - 208; + } else { + sdn = tishri1After + day - lengthOfAdarIAndII - 178; + } + break; + + default: + /* It is Adar II or later - don't need the year length. */ + FindStartOfYear(year + 1, &metonicCycle, &metonicYear, + &moladDay, &moladHalakim, &tishri1After); + + switch (month) { + case 7: + sdn = tishri1After + day - 207; + break; + case 8: + sdn = tishri1After + day - 178; + break; + case 9: + sdn = tishri1After + day - 148; + break; + case 10: + sdn = tishri1After + day - 119; + break; + case 11: + sdn = tishri1After + day - 89; + break; + case 12: + sdn = tishri1After + day - 60; + break; + case 13: + sdn = tishri1After + day - 30; + break; + default: + return (0); + } + } + return (sdn + JEWISH_SDN_OFFSET); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/calendar/julian.c b/ext/calendar/julian.c new file mode 100644 index 0000000..17e7bcb --- /dev/null +++ b/ext/calendar/julian.c @@ -0,0 +1,263 @@ +/* $selId: julian.c,v 2.0 1995/10/24 01:13:06 lees Exp $ + * Copyright 1993-1995, Scott E. Lee, all rights reserved. + * Permission granted to use, copy, modify, distribute and sell so long as + * the above copyright and this permission statement are retained in all + * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. + */ + +/************************************************************************** + * + * These are the externally visible components of this file: + * + * void + * SdnToJulian( + * long int sdn, + * int *pYear, + * int *pMonth, + * int *pDay); + * + * Convert a SDN to a Julian calendar date. If the input SDN is less than + * 1, the three output values will all be set to zero, otherwise *pYear + * will be >= -4713 and != 0; *pMonth will be in the range 1 to 12 + * inclusive; *pDay will be in the range 1 to 31 inclusive. + * + * long int + * JulianToSdn( + * int inputYear, + * int inputMonth, + * int inputDay); + * + * Convert a Julian calendar date to a SDN. Zero is returned when the + * input date is detected as invalid or out of the supported range. The + * return value will be > 0 for all valid, supported dates, but there are + * some invalid dates that will return a positive value. To verify that a + * date is valid, convert it to SDN and then back and compare with the + * original. + * + * VALID RANGE + * + * 4713 B.C. to at least 10000 A.D. + * + * Although this software can handle dates all the way back to 4713 + * B.C., such use may not be meaningful. The calendar was created in + * 46 B.C., but the details did not stabilize until at least 8 A.D., + * and perhaps as late at the 4th century. Also, the beginning of a + * year varied from one culture to another - not all accepted January + * as the first month. + * + * CALENDAR OVERVIEW + * + * Julias Ceasar created the calendar in 46 B.C. as a modified form of + * the old Roman republican calendar which was based on lunar cycles. + * The new Julian calendar set fixed lengths for the months, abandoning + * the lunar cycle. It also specified that there would be exactly 12 + * months per year and 365.25 days per year with every 4th year being a + * leap year. + * + * Note that the current accepted value for the tropical year is + * 365.242199 days, not 365.25. This lead to an 11 day shift in the + * calendar with respect to the seasons by the 16th century when the + * Gregorian calendar was created to replace the Julian calendar. + * + * The difference between the Julian and today's Gregorian calendar is + * that the Gregorian does not make centennial years leap years unless + * they are a multiple of 400, which leads to a year of 365.2425 days. + * In other words, in the Gregorian calendar, 1700, 1800 and 1900 are + * not leap years, but 2000 is. All centennial years are leap years in + * the Julian calendar. + * + * The details are unknown, but the lengths of the months were adjusted + * until they finally stablized in 8 A.D. with their current lengths: + * + * January 31 + * February 28/29 + * March 31 + * April 30 + * May 31 + * June 30 + * Quintilis/July 31 + * Sextilis/August 31 + * September 30 + * October 31 + * November 30 + * December 31 + * + * In the early days of the calendar, the days of the month were not + * numbered as we do today. The numbers ran backwards (decreasing) and + * were counted from the Ides (15th of the month - which in the old + * Roman republican lunar calendar would have been the full moon) or + * from the Nonae (9th day before the Ides) or from the beginning of + * the next month. + * + * In the early years, the beginning of the year varied, sometimes + * based on the ascension of rulers. It was not always the first of + * January. + * + * Also, today's epoch, 1 A.D. or the birth of Jesus Christ, did not + * come into use until several centuries later when Christianity became + * a dominant religion. + * + * ALGORITHMS + * + * The calculations are based on two different cycles: a 4 year cycle + * of leap years and a 5 month cycle of month lengths. + * + * The 5 month cycle is used to account for the varying lengths of + * months. You will notice that the lengths alternate between 30 and + * 31 days, except for three anomalies: both July and August have 31 + * days, both December and January have 31, and February is less than + * 30. Starting with March, the lengths are in a cycle of 5 months + * (31, 30, 31, 30, 31): + * + * Mar 31 days \ + * Apr 30 days | + * May 31 days > First cycle + * Jun 30 days | + * Jul 31 days / + * + * Aug 31 days \ + * Sep 30 days | + * Oct 31 days > Second cycle + * Nov 30 days | + * Dec 31 days / + * + * Jan 31 days \ + * Feb 28/9 days | + * > Third cycle (incomplete) + * + * For this reason the calculations (internally) assume that the year + * starts with March 1. + * + * TESTING + * + * This algorithm has been tested from the year 4713 B.C. to 10000 A.D. + * The source code of the verification program is included in this + * package. + * + * REFERENCES + * + * Conversions Between Calendar Date and Julian Day Number by Robert J. + * Tantzen, Communications of the Association for Computing Machinery + * August 1963. (Also published in Collected Algorithms from CACM, + * algorithm number 199). [Note: the published algorithm is for the + * Gregorian calendar, but was adjusted to use the Julian calendar's + * simpler leap year rule.] + * + **************************************************************************/ + +#include "sdncal.h" +#include <limits.h> + +#define JULIAN_SDN_OFFSET 32083 +#define DAYS_PER_5_MONTHS 153 +#define DAYS_PER_4_YEARS 1461 + +void SdnToJulian( + long int sdn, + int *pYear, + int *pMonth, + int *pDay) +{ + int year; + int month; + int day; + long int temp; + int dayOfYear; + + if (sdn <= 0) { + goto fail; + } + /* Check for overflow */ + if (sdn > (LONG_MAX - JULIAN_SDN_OFFSET * 4 + 1) / 4 || sdn < LONG_MIN / 4) { + goto fail; + } + temp = sdn * 4 + (JULIAN_SDN_OFFSET * 4 - 1); + + /* Calculate the year and day of year (1 <= dayOfYear <= 366). */ + { + long yearl = temp / DAYS_PER_4_YEARS; + if (yearl > INT_MAX || yearl < INT_MIN) { + goto fail; + } + year = (int) yearl; + } + dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1; + + /* Calculate the month and day of month. */ + temp = dayOfYear * 5 - 3; + month = temp / DAYS_PER_5_MONTHS; + day = (temp % DAYS_PER_5_MONTHS) / 5 + 1; + + /* Convert to the normal beginning of the year. */ + if (month < 10) { + month += 3; + } else { + year += 1; + month -= 9; + } + + /* Adjust to the B.C./A.D. type numbering. */ + year -= 4800; + if (year <= 0) + year--; + + *pYear = year; + *pMonth = month; + *pDay = day; + return; + +fail: + *pYear = 0; + *pMonth = 0; + *pDay = 0; +} + +long int JulianToSdn( + int inputYear, + int inputMonth, + int inputDay) +{ + int year; + int month; + + /* check for invalid dates */ + if (inputYear == 0 || inputYear < -4713 || + inputMonth <= 0 || inputMonth > 12 || + inputDay <= 0 || inputDay > 31) { + return (0); + } + /* check for dates before SDN 1 (Jan 2, 4713 B.C.) */ + if (inputYear == -4713) { + if (inputMonth == 1 && inputDay == 1) { + return (0); + } + } + /* Make year always a positive number. */ + if (inputYear < 0) { + year = inputYear + 4801; + } else { + year = inputYear + 4800; + } + + /* Adjust the start of the year. */ + if (inputMonth > 2) { + month = inputMonth - 3; + } else { + month = inputMonth + 9; + year--; + } + + return ((year * DAYS_PER_4_YEARS) / 4 + + (month * DAYS_PER_5_MONTHS + 2) / 5 + + inputDay + - JULIAN_SDN_OFFSET); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/calendar/package.xml b/ext/calendar/package.xml new file mode 100644 index 0000000..82d0675 --- /dev/null +++ b/ext/calendar/package.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE package SYSTEM "../pear/package.dtd"> +<package> + <name>calendar</name> + <summary>Date conversion between different calendar formats</summary> + <maintainers> + <maintainer> + <user>hholzgra</user> + <name>Hartmut Holzgraefe</name> + <email>hartmut@php.net</email> + <role>lead</role> + </maintainer> + <maintainer> + <user>shane</user> + <name>Shane Caraveo</name> + <role>developer</role> + <email>shane@caraveo.com</email> + </maintainer> + <maintainer> + <user>colin</user> + <name>Colin Viebrock</name> + <role>developer</role> + <email>colin@easydns.com</email> + </maintainer> + <maintainer> + <user>wez</user> + <name>Wez Furlong</name> + <role>developer</role> + <email>wez@php.net</email> + </maintainer> + </maintainers> + <description> +The calendar extension presents a series of functions to simplify +converting between different calendar formats. The intermediary or +standard it is based on is the Julian Day Count. The Julian Day Count +is a count of days starting from January 1st, 4713 B.C. To convert +between calendar systems, you must first convert to Julian Day Count, +then to the calendar system of your choice. Julian Day Count is very +different from the Julian Calendar! + </description> + <license>PHP</license> + <release> + <state>beta</state> + <version>5.0.0rc1</version> + <date>2004-03-19</date> + <notes> +package.xml added to support installation using pear installer + </notes> + <filelist> + <file role="doc" name="CREDITS"/> + <file role="src" name="config.m4"/> + <file role="src" name="config.w32"/> + <file role="src" name="cal_unix.c"/> + <file role="src" name="calendar.c"/> + <file role="src" name="dow.c"/> + <file role="src" name="easter.c"/> + <file role="src" name="french.c"/> + <file role="src" name="gregor.c"/> + <file role="src" name="jewish.c"/> + <file role="src" name="julian.c"/> + <file role="src" name="package.xml"/> + <file role="src" name="php_calendar.h"/> + <file role="src" name="sdncal.h"/> + <file role="test" name="tests/jdtojewish.phpt"/> + </filelist> + <deps> + <dep type="php" rel="ge" version="5" /> + </deps> + </release> +</package> +<!-- +vim:et:ts=1:sw=1 +--> diff --git a/ext/calendar/php_calendar.h b/ext/calendar/php_calendar.h new file mode 100644 index 0000000..e353fab --- /dev/null +++ b/ext/calendar/php_calendar.h @@ -0,0 +1,48 @@ +#ifndef PHP_CALENDAR_H +#define PHP_CALENDAR_H + +extern zend_module_entry calendar_module_entry; +#define calendar_module_ptr &calendar_module_entry + +/* Functions */ + +PHP_MINIT_FUNCTION(calendar); +PHP_MINFO_FUNCTION(calendar); + +PHP_FUNCTION(jdtogregorian); +PHP_FUNCTION(gregoriantojd); +PHP_FUNCTION(jdtojulian); +PHP_FUNCTION(juliantojd); +PHP_FUNCTION(jdtojewish); +PHP_FUNCTION(jewishtojd); +PHP_FUNCTION(jdtofrench); +PHP_FUNCTION(frenchtojd); +PHP_FUNCTION(jddayofweek); +PHP_FUNCTION(jdmonthname); +PHP_FUNCTION(easter_days); +PHP_FUNCTION(easter_date); +PHP_FUNCTION(unixtojd); +PHP_FUNCTION(jdtounix); +PHP_FUNCTION(cal_from_jd); +PHP_FUNCTION(cal_to_jd); +PHP_FUNCTION(cal_days_in_month); +PHP_FUNCTION(cal_info); + +#define phpext_calendar_ptr calendar_module_ptr + +/* + * Specifying the easter calculation method + * + * DEFAULT is Anglican, ie. use Julian calendar before 1753 + * and Gregorian after that. With ROMAN, the cutoff year is 1582. + * ALWAYS_GREGORIAN and ALWAYS_JULIAN force the calendar + * regardless of date. + * + */ + +#define CAL_EASTER_DEFAULT 0 +#define CAL_EASTER_ROMAN 1 +#define CAL_EASTER_ALWAYS_GREGORIAN 2 +#define CAL_EASTER_ALWAYS_JULIAN 3 + +#endif diff --git a/ext/calendar/sdncal.h b/ext/calendar/sdncal.h new file mode 100644 index 0000000..81328d1 --- /dev/null +++ b/ext/calendar/sdncal.h @@ -0,0 +1,97 @@ +#ifndef SDNCAL_H +#define SDNCAL_H +/* + * This code has been modified for use with PHP + * by Shane Caraveo shane@caraveo.com + * see below for more details + * + */ + +/* $selId: sdncal.h,v 2.0 1995/10/24 01:13:06 lees Exp $ + * Copyright 1993-1995, Scott E. Lee, all rights reserved. + * Permission granted to use, copy, modify, distribute and sell so long as + * the above copyright and this permission statement are retained in all + * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. + */ + +/************************************************************************** + * + * This package defines a set of routines that convert calendar dates to + * and from a serial day number (SDN). The SDN is a serial numbering of + * days where SDN 1 is November 25, 4714 BC in the Gregorian calendar and + * SDN 2447893 is January 1, 1990. This system of day numbering is + * sometimes referred to as Julian days, but to avoid confusion with the + * Julian calendar, it is referred to as serial day numbers here. The term + * Julian days is also used to mean the number of days since the beginning + * of the current year. + * + * The SDN can be used as an intermediate step in converting from one + * calendar system to another (such as Gregorian to Jewish). It can also + * be used for date computations such as easily comparing two dates, + * determining the day of the week, finding the date of yesterday or + * calculating the number of days between two dates. + * + * When using this software on 16 bit systems, be careful to store SDNs in + * a long int, because it will not fit in the 16 bits that some systems + * allocate to an int. + * + * For each calendar, there are two routines provided. One converts dates + * in that calendar to SDN and the other converts SDN to calendar dates. + * The routines are named SdnTo<CALENDAR>() and <CALENDAR>ToSdn(), where + * <CALENDAR> is the name of the calendar system. + * + * SDN values less than one are not supported. If a conversion routine + * returns an SDN of zero, this means that the date given is either invalid + * or is outside the supported range for that calendar. + * + * At least some validity checks are performed on input dates. For + * example, a negative month number will result in the return of zero for + * the SDN. A returned SDN greater than one does not necessarily mean that + * the input date was valid. To determine if the date is valid, convert it + * to SDN, and if the SDN is greater than zero, convert it back to a date + * and compare to the original. For example: + * + * int y1, m1, d1; + * int y2, m2, d2; + * long int sdn; + * ... + * sdn = GregorianToSdn(y1, m1, d1); + * if (sdn > 0) { + * SdnToGregorian(sdn, &y2, &m2, &d2); + * if (y1 == y2 && m1 == m2 && d1 == d2) { + * ... date is valid ... + * } + * } + * + **************************************************************************/ + +/* Gregorian calendar conversions. */ +void SdnToGregorian(long int sdn, int *pYear, int *pMonth, int *pDay); +long int GregorianToSdn(int year, int month, int day); +extern char *MonthNameShort[13]; +extern char *MonthNameLong[13]; + +/* Julian calendar conversions. */ +void SdnToJulian(long int sdn, int *pYear, int *pMonth, int *pDay); +long int JulianToSdn(int year, int month, int day); + +/* Jewish calendar conversions. */ +void SdnToJewish(long int sdn, int *pYear, int *pMonth, int *pDay); +long int JewishToSdn(int year, int month, int day); +extern char *JewishMonthName[14]; +extern char *JewishMonthHebName[14]; + +/* French republic calendar conversions. */ +void SdnToFrench(long int sdn, int *pYear, int *pMonth, int *pDay); +long int FrenchToSdn(int inputYear, int inputMonth, int inputDay); +extern char *FrenchMonthName[14]; + +/* Islamic calendar conversions. */ +/* Not implemented yet. */ + +/* Day of week conversion. 0=Sunday, 6=Saturday */ +int DayOfWeek(long int sdn); +extern char *DayNameShort[7]; +extern char *DayNameLong[7]; + +#endif /* SDNCAL_H */ diff --git a/ext/calendar/tests/bug52744.phpt b/ext/calendar/tests/bug52744.phpt new file mode 100644 index 0000000..886086a --- /dev/null +++ b/ext/calendar/tests/bug52744.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #52744 (cal_days_in_month incorrect for December 1 BCE) +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +var_dump(cal_days_in_month(CAL_GREGORIAN, 12, -1)); +var_dump(cal_days_in_month(CAL_JULIAN, 12, -1)); +?> +--EXPECT-- +int(31) +int(31) diff --git a/ext/calendar/tests/bug53574_1.phpt b/ext/calendar/tests/bug53574_1.phpt new file mode 100644 index 0000000..51f9b42 --- /dev/null +++ b/ext/calendar/tests/bug53574_1.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug #53574 (Integer overflow in SdnToJulian; leads to segfault) +--SKIPIF-- +<?php +include 'skipif.inc'; +if (PHP_INT_SIZE != 4) { + die("skip this test is for 32bit platform only"); +} +?> +--FILE-- +<?php +$x = 882858043; + +var_dump(cal_from_jd($x, CAL_JULIAN)); +--EXPECT-- +array(9) { + ["date"]=> + string(5) "0/0/0" + ["month"]=> + int(0) + ["day"]=> + int(0) + ["year"]=> + int(0) + ["dow"]=> + int(5) + ["abbrevdayname"]=> + string(3) "Fri" + ["dayname"]=> + string(6) "Friday" + ["abbrevmonth"]=> + string(0) "" + ["monthname"]=> + string(0) "" +} + diff --git a/ext/calendar/tests/bug53574_2.phpt b/ext/calendar/tests/bug53574_2.phpt new file mode 100644 index 0000000..45e15cb --- /dev/null +++ b/ext/calendar/tests/bug53574_2.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug #53574 (Integer overflow in SdnToJulian; leads to segfault) +--SKIPIF-- +<?php +include 'skipif.inc'; +if (PHP_INT_SIZE == 4) { + die("skip this test is for 64bit platform only"); +} +?> +--FILE-- +<?php +$x = 3315881921229094912; + +var_dump(cal_from_jd($x, CAL_JULIAN)); +--EXPECT-- +array(9) { + ["date"]=> + string(5) "0/0/0" + ["month"]=> + int(0) + ["day"]=> + int(0) + ["year"]=> + int(0) + ["dow"]=> + int(3) + ["abbrevdayname"]=> + string(3) "Wed" + ["dayname"]=> + string(9) "Wednesday" + ["abbrevmonth"]=> + string(0) "" + ["monthname"]=> + string(0) "" +} + diff --git a/ext/calendar/tests/bug55797_1.phpt b/ext/calendar/tests/bug55797_1.phpt new file mode 100644 index 0000000..ffd617d --- /dev/null +++ b/ext/calendar/tests/bug55797_1.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug #55797: Integer overflow in SdnToGregorian leads to segfault (in optimized builds) +--SKIPIF-- +<?php +include 'skipif.inc'; +if (PHP_INT_SIZE != 4) { + die("skip this test is for 32bit platform only"); +} +?> +--FILE-- +<?php +$x = 882858030; + +var_dump(cal_from_jd($x, CAL_GREGORIAN)); +--EXPECTF-- +array(9) { + ["date"]=> + string(5) "0/0/0" + ["month"]=> + int(0) + ["day"]=> + int(0) + ["year"]=> + int(0) + ["dow"]=> + int(%d) + ["abbrevdayname"]=> + string(%d) "%s" + ["dayname"]=> + string(%d) "%s" + ["abbrevmonth"]=> + string(0) "" + ["monthname"]=> + string(0) "" +} + diff --git a/ext/calendar/tests/bug55797_2.phpt b/ext/calendar/tests/bug55797_2.phpt new file mode 100644 index 0000000..2a9183d --- /dev/null +++ b/ext/calendar/tests/bug55797_2.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug #55797: Integer overflow in SdnToGregorian leads to segfault (in optimized builds) +--SKIPIF-- +<?php +include 'skipif.inc'; +if (PHP_INT_SIZE == 4) { + die("skip this test is for 64bit platform only"); +} +?> +--FILE-- +<?php +$x = 9223372036854743639; + +var_dump(cal_from_jd($x, CAL_GREGORIAN)); +--EXPECTF-- +array(9) { + ["date"]=> + string(5) "0/0/0" + ["month"]=> + int(0) + ["day"]=> + int(0) + ["year"]=> + int(0) + ["dow"]=> + int(%d) + ["abbrevdayname"]=> + string(%d) "%s" + ["dayname"]=> + string(%d) "%s" + ["abbrevmonth"]=> + string(0) "" + ["monthname"]=> + string(0) "" +} + diff --git a/ext/calendar/tests/cal_days_in_month.phpt b/ext/calendar/tests/cal_days_in_month.phpt new file mode 100644 index 0000000..9aaf3ef --- /dev/null +++ b/ext/calendar/tests/cal_days_in_month.phpt @@ -0,0 +1,20 @@ +--TEST-- +cal_days_in_month() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +$num = cal_days_in_month(CAL_GREGORIAN, 8, 2003); +echo "There are $num days in August 2003\n"; +$num = cal_days_in_month(CAL_GREGORIAN, 2, 2003); +echo "There are $num days in February 2003\n"; +$num = cal_days_in_month(CAL_GREGORIAN, 2, 2004); +echo "There are $num days in February 2004\n"; +$num = cal_days_in_month(CAL_GREGORIAN, 12, 2034); +echo "There are $num days in December 2034\n"; +?> +--EXPECT-- +There are 31 days in August 2003 +There are 28 days in February 2003 +There are 29 days in February 2004 +There are 31 days in December 2034 diff --git a/ext/calendar/tests/cal_from_jd.phpt b/ext/calendar/tests/cal_from_jd.phpt new file mode 100644 index 0000000..9614522 --- /dev/null +++ b/ext/calendar/tests/cal_from_jd.phpt @@ -0,0 +1,60 @@ +--TEST-- +cal_from_jd() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +print_r(cal_from_jd(1748326, CAL_GREGORIAN)); +print_r(cal_from_jd(1748324, CAL_JULIAN)); +print_r(cal_from_jd( 374867, CAL_JEWISH)); +print_r(cal_from_jd( 0, CAL_FRENCH)); +?> +--EXPECT-- +Array +( + [date] => 8/26/74 + [month] => 8 + [day] => 26 + [year] => 74 + [dow] => 0 + [abbrevdayname] => Sun + [dayname] => Sunday + [abbrevmonth] => Aug + [monthname] => August +) +Array +( + [date] => 8/26/74 + [month] => 8 + [day] => 26 + [year] => 74 + [dow] => 5 + [abbrevdayname] => Fri + [dayname] => Friday + [abbrevmonth] => Aug + [monthname] => August +) +Array +( + [date] => 8/26/74 + [month] => 8 + [day] => 26 + [year] => 74 + [dow] => 4 + [abbrevdayname] => Thu + [dayname] => Thursday + [abbrevmonth] => Nisan + [monthname] => Nisan +) +Array +( + [date] => 0/0/0 + [month] => 0 + [day] => 0 + [year] => 0 + [dow] => 1 + [abbrevdayname] => Mon + [dayname] => Monday + [abbrevmonth] => + [monthname] => +) diff --git a/ext/calendar/tests/cal_info.phpt b/ext/calendar/tests/cal_info.phpt new file mode 100644 index 0000000..2e3e612 --- /dev/null +++ b/ext/calendar/tests/cal_info.phpt @@ -0,0 +1,216 @@ +--TEST-- +cal_info() +--INI-- +date.timezone=UTC +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php + print_r(cal_info()); + print_r(cal_info(1)); + print_r(cal_info(99999)); +?> +--EXPECTF-- +Array +( + [0] => Array + ( + [months] => Array + ( + [1] => January + [2] => February + [3] => March + [4] => April + [5] => May + [6] => June + [7] => July + [8] => August + [9] => September + [10] => October + [11] => November + [12] => December + ) + + [abbrevmonths] => Array + ( + [1] => Jan + [2] => Feb + [3] => Mar + [4] => Apr + [5] => May + [6] => Jun + [7] => Jul + [8] => Aug + [9] => Sep + [10] => Oct + [11] => Nov + [12] => Dec + ) + + [maxdaysinmonth] => 31 + [calname] => Gregorian + [calsymbol] => CAL_GREGORIAN + ) + + [1] => Array + ( + [months] => Array + ( + [1] => January + [2] => February + [3] => March + [4] => April + [5] => May + [6] => June + [7] => July + [8] => August + [9] => September + [10] => October + [11] => November + [12] => December + ) + + [abbrevmonths] => Array + ( + [1] => Jan + [2] => Feb + [3] => Mar + [4] => Apr + [5] => May + [6] => Jun + [7] => Jul + [8] => Aug + [9] => Sep + [10] => Oct + [11] => Nov + [12] => Dec + ) + + [maxdaysinmonth] => 31 + [calname] => Julian + [calsymbol] => CAL_JULIAN + ) + + [2] => Array + ( + [months] => Array + ( + [1] => Tishri + [2] => Heshvan + [3] => Kislev + [4] => Tevet + [5] => Shevat + [6] => AdarI + [7] => AdarII + [8] => Nisan + [9] => Iyyar + [10] => Sivan + [11] => Tammuz + [12] => Av + [13] => Elul + ) + + [abbrevmonths] => Array + ( + [1] => Tishri + [2] => Heshvan + [3] => Kislev + [4] => Tevet + [5] => Shevat + [6] => AdarI + [7] => AdarII + [8] => Nisan + [9] => Iyyar + [10] => Sivan + [11] => Tammuz + [12] => Av + [13] => Elul + ) + + [maxdaysinmonth] => 30 + [calname] => Jewish + [calsymbol] => CAL_JEWISH + ) + + [3] => Array + ( + [months] => Array + ( + [1] => Vendemiaire + [2] => Brumaire + [3] => Frimaire + [4] => Nivose + [5] => Pluviose + [6] => Ventose + [7] => Germinal + [8] => Floreal + [9] => Prairial + [10] => Messidor + [11] => Thermidor + [12] => Fructidor + [13] => Extra + ) + + [abbrevmonths] => Array + ( + [1] => Vendemiaire + [2] => Brumaire + [3] => Frimaire + [4] => Nivose + [5] => Pluviose + [6] => Ventose + [7] => Germinal + [8] => Floreal + [9] => Prairial + [10] => Messidor + [11] => Thermidor + [12] => Fructidor + [13] => Extra + ) + + [maxdaysinmonth] => 30 + [calname] => French + [calsymbol] => CAL_FRENCH + ) + +) +Array +( + [months] => Array + ( + [1] => January + [2] => February + [3] => March + [4] => April + [5] => May + [6] => June + [7] => July + [8] => August + [9] => September + [10] => October + [11] => November + [12] => December + ) + + [abbrevmonths] => Array + ( + [1] => Jan + [2] => Feb + [3] => Mar + [4] => Apr + [5] => May + [6] => Jun + [7] => Jul + [8] => Aug + [9] => Sep + [10] => Oct + [11] => Nov + [12] => Dec + ) + + [maxdaysinmonth] => 31 + [calname] => Julian + [calsymbol] => CAL_JULIAN +) + +Warning: cal_info(): invalid calendar ID 99999. in %s on line %d diff --git a/ext/calendar/tests/cal_to_jd.phpt b/ext/calendar/tests/cal_to_jd.phpt new file mode 100644 index 0000000..fde1e0b --- /dev/null +++ b/ext/calendar/tests/cal_to_jd.phpt @@ -0,0 +1,16 @@ +--TEST-- +cal_to_jd() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo cal_to_jd(CAL_GREGORIAN, 8, 26, 74), "\n"; +echo cal_to_jd(CAL_JULIAN, 8, 26, 74), "\n"; +echo cal_to_jd(CAL_JEWISH, 8, 26, 74), "\n"; +echo cal_to_jd(CAL_FRENCH, 8, 26, 74), "\n"; +?> +--EXPECT-- +1748326 +1748324 +374867 +0 diff --git a/ext/calendar/tests/easter_date.phpt b/ext/calendar/tests/easter_date.phpt new file mode 100644 index 0000000..9222d3c --- /dev/null +++ b/ext/calendar/tests/easter_date.phpt @@ -0,0 +1,21 @@ +--TEST-- +easter_date() +--INI-- +date.timezone=UTC +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +putenv('TZ=UTC'); +echo date("Y-m-d", easter_date(2000))."\n"; +echo date("Y-m-d", easter_date(2001))."\n"; +echo date("Y-m-d", easter_date(2002))."\n"; +echo date("Y-m-d", easter_date(1492))."\n"; +?> +--EXPECTF-- +2000-04-23 +2001-04-15 +2002-03-31 + +Warning: easter_date(): This function is only valid for years between 1970 and 2037 inclusive in %s on line %d +1970-01-01 diff --git a/ext/calendar/tests/easter_days.phpt b/ext/calendar/tests/easter_days.phpt new file mode 100644 index 0000000..04aa7ae --- /dev/null +++ b/ext/calendar/tests/easter_days.phpt @@ -0,0 +1,14 @@ +--TEST-- +easter_days() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo easter_days(1999), "\n"; +echo easter_days(1492), "\n"; +echo easter_days(1913), "\n"; +?> +--EXPECT-- +14 +32 +2 diff --git a/ext/calendar/tests/frenchtojd.phpt b/ext/calendar/tests/frenchtojd.phpt new file mode 100644 index 0000000..73addb6 --- /dev/null +++ b/ext/calendar/tests/frenchtojd.phpt @@ -0,0 +1,16 @@ +--TEST-- +frenchtojd() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo frenchtojd(-1,-1,-1), "\n"; +echo frenchtojd(0,0,0), "\n"; +echo frenchtojd(1,1,1), "\n"; +echo frenchtojd(14,31,15), "\n"; +?> +--EXPECT-- +0 +0 +2375840 +0 diff --git a/ext/calendar/tests/gregoriantojd.phpt b/ext/calendar/tests/gregoriantojd.phpt new file mode 100644 index 0000000..ec3628e --- /dev/null +++ b/ext/calendar/tests/gregoriantojd.phpt @@ -0,0 +1,18 @@ +--TEST-- +gregoriantojd() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo gregoriantojd( 0, 0, 0). "\n"; +echo gregoriantojd( 1, 1, 1582). "\n"; +echo gregoriantojd(10, 5, 1582). "\n"; +echo gregoriantojd( 1, 1, 1970). "\n"; +echo gregoriantojd( 1, 1, 2999). "\n"; +?> +--EXPECT-- +0 +2298874 +2299151 +2440588 +2816423
\ No newline at end of file diff --git a/ext/calendar/tests/jddayofweek.phpt b/ext/calendar/tests/jddayofweek.phpt new file mode 100644 index 0000000..c33d598 --- /dev/null +++ b/ext/calendar/tests/jddayofweek.phpt @@ -0,0 +1,130 @@ +--TEST-- +jddayofweek() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +foreach (array(2440588, 2452162, 2453926, -1000) as $jd) { + echo "### JD $jd ###\n"; + for ($mode = 0; $mode <= 2; $mode++) { + echo "--- mode $mode ---\n"; + for ($offset = 0; $offset <= 7; $offset++) { + echo jddayofweek($jd + $offset, $mode). "\n"; + } + } +} +?> +--EXPECT-- +### JD 2440588 ### +--- mode 0 --- +4 +5 +6 +0 +1 +2 +3 +4 +--- mode 1 --- +Thursday +Friday +Saturday +Sunday +Monday +Tuesday +Wednesday +Thursday +--- mode 2 --- +Thu +Fri +Sat +Sun +Mon +Tue +Wed +Thu +### JD 2452162 ### +--- mode 0 --- +0 +1 +2 +3 +4 +5 +6 +0 +--- mode 1 --- +Sunday +Monday +Tuesday +Wednesday +Thursday +Friday +Saturday +Sunday +--- mode 2 --- +Sun +Mon +Tue +Wed +Thu +Fri +Sat +Sun +### JD 2453926 ### +--- mode 0 --- +0 +1 +2 +3 +4 +5 +6 +0 +--- mode 1 --- +Sunday +Monday +Tuesday +Wednesday +Thursday +Friday +Saturday +Sunday +--- mode 2 --- +Sun +Mon +Tue +Wed +Thu +Fri +Sat +Sun +### JD -1000 ### +--- mode 0 --- +2 +3 +4 +5 +6 +0 +1 +2 +--- mode 1 --- +Tuesday +Wednesday +Thursday +Friday +Saturday +Sunday +Monday +Tuesday +--- mode 2 --- +Tue +Wed +Thu +Fri +Sat +Sun +Mon +Tue + diff --git a/ext/calendar/tests/jdmonthname.phpt b/ext/calendar/tests/jdmonthname.phpt new file mode 100644 index 0000000..d05d3c5 --- /dev/null +++ b/ext/calendar/tests/jdmonthname.phpt @@ -0,0 +1,314 @@ +--TEST-- +jdmonthname() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +foreach (array(2440588, 2452162, 2453926) as $jd) { + echo "### JD $jd ###\n"; + for ($mode = 0; $mode <= 6; $mode++) { + echo "--- mode $mode ---\n"; + for ($offset = 0; $offset <= 12; $offset++) { + echo jdmonthname($jd + $offset * 30, $mode). "\n"; + } + } +} +?> +--EXPECT-- +### JD 2440588 ### +--- mode 0 --- +Jan +Jan +Mar +Apr +May +May +Jun +Jul +Aug +Sep +Oct +Nov +Dec +--- mode 1 --- +January +January +March +April +May +May +June +July +August +September +October +November +December +--- mode 2 --- +Dec +Jan +Feb +Mar +Apr +May +Jun +Jul +Aug +Sep +Oct +Nov +Dec +--- mode 3 --- +December +January +February +March +April +May +June +July +August +September +October +November +December +--- mode 4 --- +Tevet +Shevat +AdarI +AdarII +Nisan +Iyyar +Sivan +Tammuz +Av +Elul +Tishri +Heshvan +Kislev +--- mode 5 --- + + + + + + + + + + + + + +--- mode 6 --- +Jan +Jan +Mar +Apr +May +May +Jun +Jul +Aug +Sep +Oct +Nov +Dec +### JD 2452162 ### +--- mode 0 --- +Sep +Oct +Nov +Dec +Jan +Feb +Mar +Apr +May +Jun +Jul +Aug +Sep +--- mode 1 --- +September +October +November +December +January +February +March +April +May +June +July +August +September +--- mode 2 --- +Aug +Sep +Oct +Nov +Dec +Jan +Feb +Mar +Apr +May +Jun +Jul +Aug +--- mode 3 --- +August +September +October +November +December +January +February +March +April +May +June +July +August +--- mode 4 --- +Elul +Tishri +Heshvan +Kislev +Tevet +Shevat +AdarI +Nisan +Iyyar +Sivan +Tammuz +Av +Elul +--- mode 5 --- + + + + + + + + + + + + + +--- mode 6 --- +Sep +Oct +Nov +Dec +Jan +Feb +Mar +Apr +May +Jun +Jul +Aug +Sep +### JD 2453926 ### +--- mode 0 --- +Jul +Aug +Sep +Oct +Nov +Dec +Jan +Feb +Mar +Apr +May +Jun +Jul +--- mode 1 --- +July +August +September +October +November +December +January +February +March +April +May +June +July +--- mode 2 --- +Jun +Jul +Aug +Sep +Oct +Nov +Dec +Jan +Feb +Mar +Apr +May +Jun +--- mode 3 --- +June +July +August +September +October +November +December +January +February +March +April +May +June +--- mode 4 --- +Tammuz +Av +Elul +Tishri +Heshvan +Kislev +Tevet +Shevat +AdarI +Nisan +Iyyar +Sivan +Tammuz +--- mode 5 --- + + + + + + + + + + + + + +--- mode 6 --- +Jul +Aug +Sep +Oct +Nov +Dec +Jan +Feb +Mar +Apr +May +Jun +Jul diff --git a/ext/calendar/tests/jdtofrench.phpt b/ext/calendar/tests/jdtofrench.phpt new file mode 100644 index 0000000..2794409 --- /dev/null +++ b/ext/calendar/tests/jdtofrench.phpt @@ -0,0 +1,20 @@ +--TEST-- +jdtofrench() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo jdtofrench(0). "\n"; +echo jdtofrench(2375840). "\n"; +echo jdtofrench(2375850). "\n"; +echo jdtofrench(2375940). "\n"; +echo jdtofrench(2376345). "\n"; +echo jdtofrench(2385940). "\n"; +?> +--EXPECT-- +0/0/0 +1/1/1 +1/11/1 +4/11/1 +5/21/2 +0/0/0
\ No newline at end of file diff --git a/ext/calendar/tests/jdtogregorian.phpt b/ext/calendar/tests/jdtogregorian.phpt new file mode 100644 index 0000000..6b1956f --- /dev/null +++ b/ext/calendar/tests/jdtogregorian.phpt @@ -0,0 +1,18 @@ +--TEST-- +jdtogregorian() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo jdtogregorian(0). "\n"; +echo jdtogregorian(2298874). "\n"; +echo jdtogregorian(2299151). "\n"; +echo jdtogregorian(2440588). "\n"; +echo jdtogregorian(2816423). "\n"; +?> +--EXPECT-- +0/0/0 +1/1/1582 +10/5/1582 +1/1/1970 +1/1/2999
\ No newline at end of file diff --git a/ext/calendar/tests/jdtojewish.phpt b/ext/calendar/tests/jdtojewish.phpt new file mode 100644 index 0000000..484b957 --- /dev/null +++ b/ext/calendar/tests/jdtojewish.phpt @@ -0,0 +1,30 @@ +--TEST-- +jdtojewish() function +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php + +var_dump(jdtojewish(gregoriantojd(10,28,2002))."\r\n". + jdtojewish(gregoriantojd(10,28,2002),true)."\r\n". + jdtojewish(gregoriantojd(10,28,2002),true, CAL_JEWISH_ADD_ALAFIM_GERESH)."\r\n". + jdtojewish(gregoriantojd(10,28,2002),true, CAL_JEWISH_ADD_ALAFIM)."\r\n". + jdtojewish(gregoriantojd(10,28,2002),true, CAL_JEWISH_ADD_ALAFIM_GERESH+CAL_JEWISH_ADD_ALAFIM)."\r\n". + jdtojewish(gregoriantojd(10,28,2002),true, CAL_JEWISH_ADD_GERESHAYIM)."\r\n". + jdtojewish(gregoriantojd(10,8,2002),true, CAL_JEWISH_ADD_GERESHAYIM)."\r\n". + jdtojewish(gregoriantojd(10,8,2002),true, CAL_JEWISH_ADD_GERESHAYIM+CAL_JEWISH_ADD_ALAFIM_GERESH)."\r\n". + jdtojewish(gregoriantojd(10,8,2002),true, CAL_JEWISH_ADD_GERESHAYIM+CAL_JEWISH_ADD_ALAFIM)."\r\n". + jdtojewish(gregoriantojd(10,8,2002),true, CAL_JEWISH_ADD_GERESHAYIM+CAL_JEWISH_ADD_ALAFIM+CAL_JEWISH_ADD_ALAFIM_GERESH)."\r\n"); +?> +--EXPECT-- +string(184) "2/22/5763 +כב חשון התשסג +כב חשון ה'תשסג +כב חשון ה אלפים תשסג +כב חשון ה' אלפים תשסג +כ"ב חשון התשס"ג +ב' חשון התשס"ג +ב' חשון ה'תשס"ג +ב' חשון ה אלפים תשס"ג +ב' חשון ה' אלפים תשס"ג +" diff --git a/ext/calendar/tests/jdtojulian.phpt b/ext/calendar/tests/jdtojulian.phpt new file mode 100644 index 0000000..6c87aa7 --- /dev/null +++ b/ext/calendar/tests/jdtojulian.phpt @@ -0,0 +1,18 @@ +--TEST-- +jdtojulian() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo jdtojulian(0). "\n"; +echo jdtojulian(2298874). "\n"; +echo jdtojulian(2299151). "\n"; +echo jdtojulian(2440588). "\n"; +echo jdtojulian(2816423). "\n"; +?> +--EXPECT-- +0/0/0 +12/22/1581 +9/25/1582 +12/19/1969 +12/12/2998
\ No newline at end of file diff --git a/ext/calendar/tests/jdtomonthname.phpt b/ext/calendar/tests/jdtomonthname.phpt new file mode 100644 index 0000000..5c55247 --- /dev/null +++ b/ext/calendar/tests/jdtomonthname.phpt @@ -0,0 +1,85 @@ +--TEST-- +jdtomonthname() test +--SKIPIF-- +<?php if (!extension_loaded("calendar")) print "skip"; ?> +--FILE-- +<?php + +$jd_days = Array( + 2453396, + 2440588, + -1, + array(), + 10000000 + ); + +foreach ($jd_days as $jd_day) { + echo "=== ", $jd_day, "\n"; + var_dump(jdmonthname($jd_day,0)); + var_dump(jdmonthname($jd_day,1)); + var_dump(jdmonthname($jd_day,2)); + var_dump(jdmonthname($jd_day,3)); + var_dump(jdmonthname($jd_day,4)); + var_dump(jdmonthname($jd_day,5)); + echo "\n"; +} + +echo "Done\n"; + +?> +--EXPECTF-- +=== 2453396 +string(3) "Jan" +string(7) "January" +string(3) "Jan" +string(7) "January" +string(6) "Shevat" +string(0) "" + +=== 2440588 +string(3) "Jan" +string(7) "January" +string(3) "Dec" +string(8) "December" +string(5) "Tevet" +string(0) "" + +=== -1 +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" + +=== +Notice: Array to string conversion in %sjdtomonthname.php on line %d +Array + +Warning: jdmonthname() expects parameter 1 to be long, array given in %s on line %d +bool(false) + +Warning: jdmonthname() expects parameter 1 to be long, array given in %s on line %d +bool(false) + +Warning: jdmonthname() expects parameter 1 to be long, array given in %s on line %d +bool(false) + +Warning: jdmonthname() expects parameter 1 to be long, array given in %s on line %d +bool(false) + +Warning: jdmonthname() expects parameter 1 to be long, array given in %s on line %d +bool(false) + +Warning: jdmonthname() expects parameter 1 to be long, array given in %s on line %d +bool(false) + +=== 10000000 +string(3) "Dec" +string(8) "December" +string(3) "Jul" +string(4) "July" +string(6) "Tishri" +string(0) "" + +Done diff --git a/ext/calendar/tests/jdtounix.phpt b/ext/calendar/tests/jdtounix.phpt new file mode 100644 index 0000000..8d85543 --- /dev/null +++ b/ext/calendar/tests/jdtounix.phpt @@ -0,0 +1,16 @@ +--TEST-- +jdtounix() +--INI-- +date.timezone=UTC +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo date("Y-m-d",jdtounix(2440588)). "\n"; +echo date("Y-m-d",jdtounix(2452162)). "\n"; +echo date("Y-m-d",jdtounix(2453926)). "\n"; +?> +--EXPECT-- +1970-01-01 +2001-09-09 +2006-07-09 diff --git a/ext/calendar/tests/jewishtojd.phpt b/ext/calendar/tests/jewishtojd.phpt new file mode 100644 index 0000000..a9a2ff0 --- /dev/null +++ b/ext/calendar/tests/jewishtojd.phpt @@ -0,0 +1,16 @@ +--TEST-- +jewishtojd() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo jewishtojd(-1,-1,-1). "\n"; +echo jewishtojd(0,0,0). "\n"; +echo jewishtojd(1,1,1). "\n"; +echo jewishtojd(2,22,5763). "\n"; +?> +--EXPECT-- +0 +0 +347998 +2452576
\ No newline at end of file diff --git a/ext/calendar/tests/juliantojd.phpt b/ext/calendar/tests/juliantojd.phpt new file mode 100644 index 0000000..9563e04 --- /dev/null +++ b/ext/calendar/tests/juliantojd.phpt @@ -0,0 +1,18 @@ +--TEST-- +juliantojd() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--FILE-- +<?php +echo juliantojd( 0, 0, 0). "\n"; +echo juliantojd( 1, 1, 1582). "\n"; +echo juliantojd(10, 5, 1582). "\n"; +echo juliantojd( 1, 1, 1970). "\n"; +echo juliantojd( 1, 1, 2999). "\n"; +?> +--EXPECT-- +0 +2298884 +2299161 +2440601 +2816443
\ No newline at end of file diff --git a/ext/calendar/tests/skipif.inc b/ext/calendar/tests/skipif.inc new file mode 100644 index 0000000..de8e4ae --- /dev/null +++ b/ext/calendar/tests/skipif.inc @@ -0,0 +1,4 @@ +<?php +if(!extension_loaded("calendar")) + print "skip - CALENDAR extension not available"; +?> diff --git a/ext/calendar/tests/unixtojd.phpt b/ext/calendar/tests/unixtojd.phpt new file mode 100644 index 0000000..4eeb1ca --- /dev/null +++ b/ext/calendar/tests/unixtojd.phpt @@ -0,0 +1,40 @@ +--TEST-- +unixtojd() +--SKIPIF-- +<?php include 'skipif.inc'; ?> +--ENV-- +TZ=UTC +--FILE-- +<?php +// this line has no impact on test output on Windows +putenv('TZ=UTC'); +// getenv('TZ') returns 'UTC' here +// putenv (basic_functions.c) does call tzset() when the env var being put is 'TZ' +// -adding a call direct to GetEnvironmentVariableA just before tzset() is called to check the value of 'TZ' returns 'UTC' +// putting a call to date_default_timezone_set() here doesn't help +// +// on Windows, the only thing that gets this test to pass is to put TZ=UTC in --ENV-- section +// -since putenv() is written to call tzset() when env var is TZ, I assume that putenv("TZ=UTC") is intended to work +// and should work on all platforms(including Windows). +// easter_date.phpt passes +// -doesn't use --ENV-- section +// -uses --INI-- section with date.timezone=UTC +// -uses putenv('TZ=UTC') +// date.timezone=UTC +// -if ommitted from easter_date.phpt, outputs DATE_TZ_ERRMSG warning +// -easter_date() calls mktime() and localtime() +// -whereas unixtojd(1000000000) calls localtime(1000000000) +// -if ommitted from unixtojd.phpt, does NOT output DATE_TZ_ERRMSG +// +// unixtojd() calls php_localtime_r() which for Pacific timezone systems, returns a time -8 hours +// -this incorrect localtime is passed to the julian date conversion (GregorianToSDN) function which works (probably correctly) +// but returns -1 day from expected because its input is -1 from expected + +echo unixtojd(40000). "\n"; +echo unixtojd(1000000000). "\n"; +echo unixtojd(1152459009). "\n"; +?> +--EXPECT-- +2440588 +2452162 +2453926 |