diff --git a/ext/date/lib/interval.c b/ext/date/lib/interval.c index 96867ba2b797a..909009d41d745 100644 --- a/ext/date/lib/interval.c +++ b/ext/date/lib/interval.c @@ -62,7 +62,7 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two) rt->s = two->s - one->s; rt->days = abs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400)); - timelib_do_rel_normalize(rt->invert ? one : two, rt); + timelib_do_rel_normalize_by_base(rt->invert ? one : two, rt); /* Restore old TZ info */ memcpy(one, &one_backup, sizeof(one_backup)); @@ -70,3 +70,37 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two) return rt; } + +#define timelib_rel_invert_member(rt, member) do { rt->member *= -1; rt->invert = !rt->invert; } while(0) + +timelib_rel_time *timelib_rel_add(timelib_rel_time *one, timelib_rel_time *two) +{ + timelib_rel_time *rt; + int one_mult, two_mult; + + if (one->have_weekday_relative || one->have_special_relative || + two->have_weekday_relative || two->have_special_relative) { + return NULL; + } + + rt = timelib_rel_time_ctor(); + + if (one->invert && two->invert) { + one_mult = two_mult = 1; + rt->invert = 1; + } else { + one_mult = one->invert ? -1 : 1; + two_mult = two->invert ? -1 : 1; + } + + rt->y = one_mult * one->y + two_mult * two->y; + rt->m = one_mult * one->m + two_mult * two->m; + rt->d = one_mult * one->d + two_mult * two->d; + rt->h = one_mult * one->h + two_mult * two->h; + rt->i = one_mult * one->i + two_mult * two->i; + rt->s = one_mult * one->s + two_mult * two->s; + + timelib_do_rel_normalize(rt); + + return rt; +} diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index dfb7dc0300763..85121fad00203 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -85,7 +85,8 @@ void timelib_strtointerval(char *s, int len, /* From tm2unixtime.c */ void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi); void timelib_do_normalize(timelib_time *base); -void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt); +void timelib_do_rel_normalize_by_base(timelib_time *base, timelib_rel_time *rt); +void timelib_do_rel_normalize(timelib_rel_time *rt); /* From unixtime2tm.c */ int timelib_apply_localtime(timelib_time *t, unsigned int localtime); @@ -138,5 +139,6 @@ int timelib_astro_rise_set_altitude(timelib_time *time, double lon, double lat, /* from interval.c */ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two); +timelib_rel_time *timelib_rel_add(timelib_rel_time *one, timelib_rel_time *two); #endif diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c index 23fe202ba9f44..33fc6d00717c3 100644 --- a/ext/date/lib/tm2unixtime.c +++ b/ext/date/lib/tm2unixtime.c @@ -168,17 +168,41 @@ static void do_adjust_for_weekday(timelib_time* time) time->relative.have_weekday_relative = 0; } -void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt) +void timelib_do_rel_normalize_by_base(timelib_time *base, timelib_rel_time *rt) { do {} while (do_range_limit(0, 60, 60, &rt->s, &rt->i)); do {} while (do_range_limit(0, 60, 60, &rt->i, &rt->h)); do {} while (do_range_limit(0, 24, 24, &rt->h, &rt->d)); do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y)); - do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d, rt->invert); do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y)); } +void timelib_do_rel_normalize(timelib_rel_time *rt) +{ + int i, hmi; + timelib_sll *members[6] = {&rt->s, &rt->i, &rt->h, &rt->d, &rt->m, &rt->y}; + timelib_sll limits[6] = {60, 60, 24, 0, 12, 0}; + + for (i = 5; i >= 0; i--) { + if (*(members[i]) != 0) { + if (*(members[i]) < 0) { + *(members[i]) *= -1; + rt->invert = !rt->invert; + } + break; + } + } + /* save highest not zero member index */ + hmi = i + 1; + for (i = 0; i < 6; i++) { + if (i == hmi) + break; + if (limits[i]) + do {} while (do_range_limit(0, limits[i], limits[i], members[i], members[i+1])); + } +} + void timelib_do_normalize(timelib_time* time) { if (time->s != TIMELIB_UNSET) do {} while (do_range_limit(0, 60, 60, &time->s, &time->i)); diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 0f8822a906006..932dbc2b27b14 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -367,6 +367,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_date_method_interval_format, 0) ZEND_ARG_INFO(0, format) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_date_method_interval_addsub, 0) + ZEND_ARG_INFO(0, interval) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_date_period_construct, 0, 0, 3) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, interval) @@ -376,6 +380,11 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_construct, 0, 0, 0) ZEND_ARG_INFO(0, interval_spec) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_date_interval_addsub, 0) + ZEND_ARG_INFO(0, interval1) + ZEND_ARG_INFO(0, interval2) +ZEND_END_ARG_INFO() /* }}} */ /* {{{ Function table */ @@ -432,6 +441,8 @@ const zend_function_entry date_functions[] = { PHP_FE(date_interval_create_from_date_string, arginfo_date_interval_create_from_date_string) PHP_FE(date_interval_format, arginfo_date_interval_format) + PHP_FE(date_interval_add, arginfo_date_interval_addsub) + PHP_FE(date_interval_sub, arginfo_date_interval_addsub) /* Options and Configuration */ PHP_FE(date_default_timezone_set, arginfo_date_default_timezone_set) @@ -516,6 +527,8 @@ const zend_function_entry date_funcs_interval[] = { PHP_ME(DateInterval, __wakeup, NULL, ZEND_ACC_PUBLIC) PHP_ME(DateInterval, __set_state, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME_MAPPING(format, date_interval_format, arginfo_date_method_interval_format, 0) + PHP_ME_MAPPING(add, date_interval_add, arginfo_date_method_interval_addsub, 0) + PHP_ME_MAPPING(sub, date_interval_sub, arginfo_date_method_interval_addsub, 0) PHP_ME_MAPPING(createFromDateString, date_interval_create_from_date_string, arginfo_date_interval_create_from_date_string, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_FE_END }; @@ -4347,6 +4360,67 @@ PHP_FUNCTION(date_interval_format) } /* }}} */ +/* {{{ proto string date_interval_add(DateInterval interval1, DateInterval interval2) + Add the intervals. +*/ +PHP_FUNCTION(date_interval_add) +{ + zval *zi1, *zi2; + php_interval_obj *diobj1, *diobj2, *diobjr; + timelib_rel_time *rtime; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &zi1, date_ce_interval, &zi2, date_ce_interval) == FAILURE) { + RETURN_FALSE; + } + diobj1 = (php_interval_obj *) zend_object_store_get_object(zi1 TSRMLS_CC); + DATE_CHECK_INITIALIZED(diobj1->initialized, DateInterval); + diobj2 = (php_interval_obj *) zend_object_store_get_object(zi2 TSRMLS_CC); + DATE_CHECK_INITIALIZED(diobj2->initialized, DateInterval); + + rtime = timelib_rel_add(diobj1->diff, diobj2->diff); + if (!rtime) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Relative interval cannot be used for interval addition"); + } + + php_date_instantiate(date_ce_interval, return_value TSRMLS_CC); + diobjr = zend_object_store_get_object(return_value TSRMLS_CC); + diobjr->diff = rtime; + diobjr->initialized = 1; +} +/* }}} */ + +/* {{{ proto string date_interval_sub(DateInterval interval1, DateInterval interval2) + Add the intervals. +*/ +PHP_FUNCTION(date_interval_sub) +{ + zval *zi1, *zi2; + php_interval_obj *diobj1, *diobj2, *diobjr; + timelib_rel_time *rtime; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &zi1, date_ce_interval, &zi2, date_ce_interval) == FAILURE) { + RETURN_FALSE; + } + diobj1 = (php_interval_obj *) zend_object_store_get_object(zi1 TSRMLS_CC); + DATE_CHECK_INITIALIZED(diobj1->initialized, DateInterval); + diobj2 = (php_interval_obj *) zend_object_store_get_object(zi2 TSRMLS_CC); + DATE_CHECK_INITIALIZED(diobj2->initialized, DateInterval); + + diobj2->diff->invert = !diobj2->diff->invert; + rtime = timelib_rel_add(diobj1->diff, diobj2->diff); + diobj2->diff->invert = !diobj2->diff->invert; + if (!rtime) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Relative interval cannot be used for interval substraction"); + } + + php_date_instantiate(date_ce_interval, return_value TSRMLS_CC); + diobjr = zend_object_store_get_object(return_value TSRMLS_CC); + diobjr->diff = rtime; + diobjr->initialized = 1; +} +/* }}} */ + + static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, long *recurrences, /*const*/ char *format, int format_length TSRMLS_DC) { timelib_time *b = NULL, *e = NULL; diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 725590136c706..be72dd9f17740 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -100,6 +100,8 @@ PHP_METHOD(DateInterval, __construct); PHP_METHOD(DateInterval, __wakeup); PHP_METHOD(DateInterval, __set_state); PHP_FUNCTION(date_interval_format); +PHP_FUNCTION(date_interval_add); +PHP_FUNCTION(date_interval_sub); PHP_FUNCTION(date_interval_create_from_date_string); PHP_METHOD(DatePeriod, __construct); diff --git a/ext/date/tests/DateInterval_add_basic.phpt b/ext/date/tests/DateInterval_add_basic.phpt new file mode 100644 index 0000000000000..a5ff58da1103d --- /dev/null +++ b/ext/date/tests/DateInterval_add_basic.phpt @@ -0,0 +1,70 @@ +--TEST-- +Test DateInterval::add() basic functionality +--CREDITS-- +Jakub Zelenka +--SKIPIF-- + +--FILE-- +diff($date12); +echo $interval1->format('%Y-%M-%D %H:%i:%s') . "\n"; + +$date21 = new DateTime('2000-01-01 00:00:00'); +$date22 = new DateTime('2001-03-10 03:40:50'); +$interval2 = $date21->diff($date22); +echo $interval2->format('%Y-%M-%D %H:%i:%s') . "\n"; + +$interval = $interval1->add($interval2); +echo $interval->format('%Y-%M-%D %H:%i:%s') . "\n"; + +$interval1->invert = 1; +$interval = $interval->add($interval1); +echo $interval->format('%Y-%M-%D %H:%i:%s') . "\n"; + +$i0 = new Dateinterval('P1Y4D'); +$i1 = new DateInterval('P1Y3D'); +$i0->invert = 1; + +$i2 = $i0->add($i1); /* -1 day */ +echo $i2->format('%y-%m-%d %h-%i-%s') . "\n"; +echo $i2->invert . "\n"; + +$i2 = $i0->sub($i1); /* -(2 years and 7 days) */ +echo $i2->format('%y-%m-%d %h-%i-%s') . "\n"; +echo $i2->invert . "\n"; + +$i0->invert = 0; +$i2 = $i0->sub($i1); +echo $i2->format('%y-%m-%d %h-%i-%s') . "\n"; +echo $i2->invert . "\n"; + +$i0 = new Dateinterval('P1Y4DT3H'); +$i1 = new DateInterval('P1Y4DT3H2M'); +$i2 = $i0->sub($i1); /* -2 minutes */ +echo $i2->format('%y-%m-%d %h-%i-%s') . "\n"; +echo $i2->invert . "\n"; + +$i2 = $i1->sub($i0); +echo $i2->format('%y-%m-%d %h-%i-%s') . "\n"; +echo $i2->invert . "\n"; + +?> +--EXPECT-- +00-00-09 22:30:23 +01-02-09 03:40:50 +01-02-19 02:11:13 +01-02-09 03:40:50 +0-0-1 0-0-0 +1 +2-0-7 0-0-0 +1 +0-0-1 0-0-0 +0 +0-0-0 0-2-0 +1 +0-0-0 0-2-0 +0 diff --git a/ext/date/tests/DateInterval_sub_basic.phpt b/ext/date/tests/DateInterval_sub_basic.phpt new file mode 100644 index 0000000000000..a25dfb17e0635 --- /dev/null +++ b/ext/date/tests/DateInterval_sub_basic.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test DateInterval::sub() basic functionality +--CREDITS-- +Jakub Zelenka +--SKIPIF-- + +--FILE-- +diff($date12); +echo $interval1->format('%Y-%M-%D %H:%i:%s') . "\n"; + +$date21 = new DateTime('2000-01-01 00:00:00'); +$date22 = new DateTime('2000-03-10 03:40:50'); +$interval2 = $date21->diff($date22); +echo $interval2->format('%Y-%M-%D %H:%i:%s') . "\n"; + +$interval = $interval1->sub($interval2); +echo $interval->format('%Y-%M-%D %H:%i:%s') . "\n"; + +?> +--EXPECT-- +01-00-09 22:30:23 +00-02-09 03:40:50 +00-10-00 18:49:33