Skip to content

Add DateInterval::add and DateInterval::sub methods #390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion ext/date/lib/interval.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,45 @@ 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));
memcpy(two, &two_backup, sizeof(two_backup));

return rt;
}

#define timelib_rel_invert_member(rt, member) do { rt->member *= -1; rt->invert = !rt->invert; } while(0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, no macros. They are a pain to debug and it's really annoying if their names are lower case too!


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;
}
4 changes: 3 additions & 1 deletion ext/date/lib/timelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
28 changes: 26 additions & 2 deletions ext/date/lib/tm2unixtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
74 changes: 74 additions & 0 deletions ext/date/php_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
};
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions ext/date/php_date.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
70 changes: 70 additions & 0 deletions ext/date/tests/DateInterval_add_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
--TEST--
Test DateInterval::add() basic functionality
--CREDITS--
Jakub Zelenka <bukka@php.net>
--SKIPIF--
<?php if (!method_exists('DateInterval', 'add')) die("skip: method doesn't exist"); ?>
--FILE--
<?php
date_default_timezone_set('UTC');

$date11 = new DateTime('2000-01-01 00:00:00');
$date12 = new DateTime('2000-01-10 22:30:23');
$interval1 = $date11->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
28 changes: 28 additions & 0 deletions ext/date/tests/DateInterval_sub_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
Test DateInterval::sub() basic functionality
--CREDITS--
Jakub Zelenka <bukka@php.net>
--SKIPIF--
<?php if (!method_exists('DateInterval', 'sub')) die("skip: method doesn't exist"); ?>
--FILE--
<?php
date_default_timezone_set('UTC');

$date11 = new DateTime('2000-01-01 00:00:00');
$date12 = new DateTime('2001-01-10 22:30:23');
$interval1 = $date11->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