From: Derick Rethans Date: Fri, 25 Apr 2008 12:35:58 +0000 (+0000) Subject: - Added new date/time functionality: X-Git-Tag: BEFORE_NEW_PARAMETER_PARSE~329 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2047fa858c1775742b84501701845b86f8a2a47f;p=php - Added new date/time functionality: . support for diffing date/times through date_diff() / DateTime::diff(). . added DateInterval class to represent the difference between two date/times. . support for parsing ISO intervals for use with DateInterval. . date_add() / DateTime::add(), date_sub() / DateTime::sub() for applying an interval to an existing date/time. - MFH: Fixed bug #44742 (timezone_offset_get() causes segmentation faults). --- diff --git a/NEWS b/NEWS index 5027353f7f..aba6cce0ff 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,11 @@ PHP NEWS . support for "first/last day of " style texts. . support for date/time strings returned by MS SQL. . support for serialization and unserialization of DateTime objects. + . support for diffing date/times through date_diff() / DateTime::diff(). + . added DateInterval class to represent the difference between two date/times. + . support for parsing ISO intervals for use with DateInterval. + . date_add() / DateTime::add(), date_sub() / DateTime::sub() for applying an + interval to an existing date/time. - Added functionality to SPL extension: . Added SPL to list of standard extensions that cannot be disabled. (Marcus) . Added ability to store associative information with objects in @@ -149,6 +154,7 @@ PHP NEWS - Fixed PECL bug #12431 (OCI8 ping functionality is broken). (Oracle Corp.) - Fixed bug #44805 (rename() function is not portable to Windows). (Pierre) +- Fixed bug #44742 (timezone_offset_get() causes segmentation faults). (Derick) - Fixed bug #44648 (Attribute names not checked for wellformedness). (Rob) - Fixed bug #44414 (Incomplete reporting about abstract methods). (Dmitry) - Fixed bug #44390 (mysqli_bind_param/bind_result and Object member variables) diff --git a/ext/date/config.m4 b/ext/date/config.m4 index cbd732f4ed..f403104a8a 100644 --- a/ext/date/config.m4 +++ b/ext/date/config.m4 @@ -6,7 +6,7 @@ sinclude(lib/timelib.m4) PHP_DATE_CFLAGS="-I@ext_builddir@/lib" timelib_sources="lib/astro.c lib/dow.c lib/parse_date.c lib/parse_tz.c - lib/timelib.c lib/tm2unixtime.c lib/unixtime2tm.c" + lib/timelib.c lib/tm2unixtime.c lib/unixtime2tm.c lib/parse_iso_intervals.c lib/interval.c" PHP_NEW_EXTENSION(date, php_date.c $timelib_sources, no,, $PHP_DATE_CFLAGS) diff --git a/ext/date/lib/README b/ext/date/lib/README index 12f09bc5e2..2b6b7f2c43 100644 --- a/ext/date/lib/README +++ b/ext/date/lib/README @@ -3,4 +3,5 @@ Regenerating Parser Make sure you use re2c 0.9.10 or higher: -/dat/dev/sf/re2c/re2c -d -b -o ext/date/lib/parse_date.c ext/date/lib/parse_date.re +re2c -d -b -o ext/date/lib/parse_date.c ext/date/lib/parse_date.re +re2c -d -b -o ext/date/lib/parse_iso_intervals.c ext/date/lib/parse_iso_intervals.re diff --git a/ext/date/lib/interval.c b/ext/date/lib/interval.c new file mode 100644 index 0000000000..b7c94af506 --- /dev/null +++ b/ext/date/lib/interval.c @@ -0,0 +1,65 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2008 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: Derick Rethans | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "timelib.h" + +timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two) +{ + timelib_rel_time *rt; + timelib_time *swp; + timelib_sll dst_h_corr = 0, dst_m_corr = 0; + + rt = timelib_rel_time_ctor(); + rt->invert = 0; + if (one->sse > two->sse) { + swp = two; + two = one; + one = swp; + rt->invert = 1; + } + + /* Calculate correction for DST change over, but only if the TZ type is ID + * and it's the same */ + if (one->zone_type == 3 && two->zone_type == 3 + && (strcmp(one->tz_info->name, two->tz_info->name) == 0) + && (one->z != two->z)) + { + dst_h_corr = (two->z - one->z) / 3600; + dst_m_corr = ((two->z - one->z) % 3600) / 60; + } + + timelib_apply_localtime(one, 0); + timelib_apply_localtime(two, 0); + + rt->y = two->y - one->y; + rt->m = two->m - one->m; + rt->d = two->d - one->d; + rt->h = two->h - one->h + dst_h_corr; + rt->i = two->i - one->i + dst_m_corr; + 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(one, rt); + + timelib_apply_localtime(one, 1); + timelib_apply_localtime(two, 1); + + return rt; +} diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c index 0fd4985014..3762a6dec9 100644 --- a/ext/date/lib/parse_date.c +++ b/ext/date/lib/parse_date.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.3 on Sun Mar 23 11:46:55 2008 */ +/* Generated by re2c 0.13.3 on Mon Apr 7 19:58:18 2008 */ #line 1 "ext/date/lib/parse_date.re" /* +----------------------------------------------------------------------+ diff --git a/ext/date/lib/parse_iso_intervals.c b/ext/date/lib/parse_iso_intervals.c new file mode 100644 index 0000000000..067f54766f --- /dev/null +++ b/ext/date/lib/parse_iso_intervals.c @@ -0,0 +1,976 @@ +/* Generated by re2c 0.13.3 on Mon Apr 7 19:58:19 2008 */ +#line 1 "ext/date/lib/parse_iso_intervals.re" +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 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: Derick Rethans | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "timelib.h" + +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#else +#include +#endif + +#if defined(_MSC_VER) +# define strtoll(s, f, b) _atoi64(s) +#elif !defined(HAVE_STRTOLL) +# if defined(HAVE_ATOLL) +# define strtoll(s, f, b) atoll(s) +# else +# define strtoll(s, f, b) strtol(s, f, b) +# endif +#endif + +#define TIMELIB_UNSET -99999 + +#define TIMELIB_SECOND 1 +#define TIMELIB_MINUTE 2 +#define TIMELIB_HOUR 3 +#define TIMELIB_DAY 4 +#define TIMELIB_MONTH 5 +#define TIMELIB_YEAR 6 + +#define EOI 257 + +#define TIMELIB_PERIOD 260 +#define TIMELIB_ISO_DATE 261 +#define TIMELIB_ERROR 999 + +typedef unsigned char uchar; + +#define BSIZE 8192 + +#define YYCTYPE uchar +#define YYCURSOR cursor +#define YYLIMIT s->lim +#define YYMARKER s->ptr +#define YYFILL(n) return EOI; + +#define RET(i) {s->cur = cursor; return i;} + +#define timelib_string_free free + +#define TIMELIB_INIT s->cur = cursor; str = timelib_string(s); ptr = str +#define TIMELIB_DEINIT timelib_string_free(str) + +#ifdef DEBUG_PARSER +#define DEBUG_OUTPUT(s) printf("%s\n", s); +#define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } } +#else +#define DEBUG_OUTPUT(s) +#define YYDEBUG(s,c) +#endif + +#include "timelib_structs.h" + +typedef struct Scanner { + int fd; + uchar *lim, *str, *ptr, *cur, *tok, *pos; + unsigned int line, len; + struct timelib_error_container *errors; + + struct timelib_time *begin; + struct timelib_time *end; + struct timelib_rel_time *period; + int recurrences; + + int have_period; + int have_recurrences; + int have_date; + int have_begin_date; + int have_end_date; +} Scanner; + +#define HOUR(a) (int)(a * 60) + +static void add_warning(Scanner *s, char *error) +{ + s->errors->warning_count++; + s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message)); + s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0; + s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0; + s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error); +} + +static void add_error(Scanner *s, char *error) +{ + s->errors->error_count++; + s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message)); + s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0; + s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0; + s->errors->error_messages[s->errors->error_count - 1].message = strdup(error); +} + +static char *timelib_string(Scanner *s) +{ + char *tmp = calloc(1, s->cur - s->tok + 1); + memcpy(tmp, s->tok, s->cur - s->tok); + + return tmp; +} + +static timelib_sll timelib_get_nr(char **ptr, int max_length) +{ + char *begin, *end, *str; + timelib_sll tmp_nr = TIMELIB_UNSET; + int len = 0; + + while ((**ptr < '0') || (**ptr > '9')) { + if (**ptr == '\0') { + return TIMELIB_UNSET; + } + ++*ptr; + } + begin = *ptr; + while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) { + ++*ptr; + ++len; + } + end = *ptr; + str = calloc(1, end - begin + 1); + memcpy(str, begin, end - begin); + tmp_nr = strtoll(str, NULL, 10); + free(str); + return tmp_nr; +} + +static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length) +{ + timelib_ull dir = 1; + + while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { + if (**ptr == '\0') { + return TIMELIB_UNSET; + } + ++*ptr; + } + + while (**ptr == '+' || **ptr == '-') + { + if (**ptr == '-') { + dir *= -1; + } + ++*ptr; + } + return dir * timelib_get_nr(ptr, max_length); +} + +static long timelib_parse_tz_cor(char **ptr) +{ + char *begin = *ptr, *end; + long tmp; + + while (**ptr != '\0') { + ++*ptr; + } + end = *ptr; + switch (end - begin) { + case 1: + case 2: + return HOUR(strtol(begin, NULL, 10)); + break; + case 3: + case 4: + if (begin[1] == ':') { + tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 2, NULL, 10); + return tmp; + } else if (begin[2] == ':') { + tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10); + return tmp; + } else { + tmp = strtol(begin, NULL, 10); + return HOUR(tmp / 100) + tmp % 100; + } + case 5: + tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10); + return tmp; + } + return 0; +} + +static void timelib_eat_spaces(char **ptr) +{ + while (**ptr == ' ' || **ptr == '\t') { + ++*ptr; + } +} + +static void timelib_eat_until_separator(char **ptr) +{ + while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) { + ++*ptr; + } +} + +static long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb) +{ + long retval = 0; + + *tz_not_found = 0; + + while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { + ++*ptr; + } + if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { + *ptr += 3; + } + if (**ptr == '+') { + ++*ptr; + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + *tz_not_found = 0; + t->dst = 0; + + retval = -1 * timelib_parse_tz_cor(ptr); + } else if (**ptr == '-') { + ++*ptr; + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + *tz_not_found = 0; + t->dst = 0; + + retval = timelib_parse_tz_cor(ptr); + } + while (**ptr == ')') { + ++*ptr; + } + return retval; +} + +#define timelib_split_free(arg) { \ + int i; \ + for (i = 0; i < arg.c; i++) { \ + free(arg.v[i]); \ + } \ + if (arg.v) { \ + free(arg.v); \ + } \ +} + +static int scan(Scanner *s) +{ + uchar *cursor = s->cur; + char *str, *ptr = NULL; + +std: + s->tok = cursor; + s->len = 0; +#line 303 "ext/date/lib/parse_iso_intervals.re" + + + +#line 286 "ext/date/lib/parse_iso_intervals.c" +{ + YYCTYPE yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + + YYDEBUG(0, *YYCURSOR); + if ((YYLIMIT - YYCURSOR) < 20) YYFILL(20); + yych = *YYCURSOR; + if (yych <= ',') { + if (yych <= 0x0A) { + if (yych <= 0x00) goto yy9; + if (yych <= 0x08) goto yy11; + if (yych <= 0x09) goto yy7; + goto yy9; + } else { + if (yych == ' ') goto yy7; + if (yych <= '+') goto yy11; + goto yy7; + } + } else { + if (yych <= 'O') { + if (yych <= '-') goto yy11; + if (yych <= '/') goto yy7; + if (yych <= '9') goto yy4; + goto yy11; + } else { + if (yych <= 'P') goto yy5; + if (yych != 'R') goto yy11; + } + } + YYDEBUG(2, *YYCURSOR); + ++YYCURSOR; + if ((yych = *YYCURSOR) <= '/') goto yy3; + if (yych <= '9') goto yy74; +yy3: + YYDEBUG(3, *YYCURSOR); +#line 396 "ext/date/lib/parse_iso_intervals.re" + { + add_error(s, "Unexpected character"); + goto std; + } +#line 361 "ext/date/lib/parse_iso_intervals.c" +yy4: + YYDEBUG(4, *YYCURSOR); + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy3; + if (yych <= '9') goto yy35; + goto yy3; +yy5: + YYDEBUG(5, *YYCURSOR); + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[0+yych] & 128) { + goto yy12; + } + if (yych == 'T') goto yy15; +yy6: + YYDEBUG(6, *YYCURSOR); +#line 343 "ext/date/lib/parse_iso_intervals.re" + { + timelib_sll nr; + int in_time = 0; + DEBUG_OUTPUT("period"); + TIMELIB_INIT; + ptr++; + do { + if ( *ptr == 'T' ) { + in_time = 1; + ptr++; + } + if ( *ptr == '\0' ) { + add_error(s, "Missing expected time part"); + break; + } + + nr = timelib_get_unsigned_nr((char **) &ptr, 4); + switch (*ptr) { + case 'Y': s->period->y = nr; break; + case 'W': s->period->d = nr * 7; break; + case 'D': s->period->d = nr; break; + case 'H': s->period->h = nr; break; + case 'S': s->period->s = nr; break; + case 'M': + if (in_time) { + s->period->i = nr; + } else { + s->period->m = nr; + } + break; + default: + add_error(s, "Undefined period specifier"); + break; + } + ptr++; + } while (*ptr); + s->have_period = 1; + TIMELIB_DEINIT; + return TIMELIB_PERIOD; + } +#line 420 "ext/date/lib/parse_iso_intervals.c" +yy7: + YYDEBUG(7, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(8, *YYCURSOR); +#line 385 "ext/date/lib/parse_iso_intervals.re" + { + goto std; + } +#line 429 "ext/date/lib/parse_iso_intervals.c" +yy9: + YYDEBUG(9, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(10, *YYCURSOR); +#line 390 "ext/date/lib/parse_iso_intervals.re" + { + s->pos = cursor; s->line++; + goto std; + } +#line 439 "ext/date/lib/parse_iso_intervals.c" +yy11: + YYDEBUG(11, *YYCURSOR); + yych = *++YYCURSOR; + goto yy3; +yy12: + YYDEBUG(12, *YYCURSOR); + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); + yych = *YYCURSOR; + YYDEBUG(13, *YYCURSOR); + if (yybm[0+yych] & 128) { + goto yy12; + } + if (yych <= 'M') { + if (yych == 'D') goto yy25; + if (yych >= 'M') goto yy27; + } else { + if (yych <= 'W') { + if (yych >= 'W') goto yy26; + } else { + if (yych == 'Y') goto yy28; + } + } +yy14: + YYDEBUG(14, *YYCURSOR); + YYCURSOR = YYMARKER; + if (yyaccept <= 0) { + goto yy3; + } else { + goto yy6; + } +yy15: + YYDEBUG(15, *YYCURSOR); + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy6; + if (yych >= ':') goto yy6; +yy16: + YYDEBUG(16, *YYCURSOR); + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + YYDEBUG(17, *YYCURSOR); + if (yych <= 'H') { + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy16; + if (yych <= 'G') goto yy14; + goto yy20; + } else { + if (yych <= 'M') { + if (yych <= 'L') goto yy14; + goto yy19; + } else { + if (yych != 'S') goto yy14; + } + } +yy18: + YYDEBUG(18, *YYCURSOR); + yych = *++YYCURSOR; + goto yy6; +yy19: + YYDEBUG(19, *YYCURSOR); + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy6; + if (yych <= '9') goto yy23; + goto yy6; +yy20: + YYDEBUG(20, *YYCURSOR); + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy6; + if (yych >= ':') goto yy6; +yy21: + YYDEBUG(21, *YYCURSOR); + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + YYDEBUG(22, *YYCURSOR); + if (yych <= 'L') { + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy21; + goto yy14; + } else { + if (yych <= 'M') goto yy19; + if (yych == 'S') goto yy18; + goto yy14; + } +yy23: + YYDEBUG(23, *YYCURSOR); + ++YYCURSOR; + if (YYLIMIT == YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + YYDEBUG(24, *YYCURSOR); + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy23; + if (yych == 'S') goto yy18; + goto yy14; +yy25: + YYDEBUG(25, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy15; + goto yy6; +yy26: + YYDEBUG(26, *YYCURSOR); + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy6; + if (yych <= '9') goto yy33; + if (yych == 'T') goto yy15; + goto yy6; +yy27: + YYDEBUG(27, *YYCURSOR); + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy6; + if (yych <= '9') goto yy31; + if (yych == 'T') goto yy15; + goto yy6; +yy28: + YYDEBUG(28, *YYCURSOR); + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy6; + if (yych <= '9') goto yy29; + if (yych == 'T') goto yy15; + goto yy6; +yy29: + YYDEBUG(29, *YYCURSOR); + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); + yych = *YYCURSOR; + YYDEBUG(30, *YYCURSOR); + if (yych <= 'D') { + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy29; + if (yych <= 'C') goto yy14; + goto yy25; + } else { + if (yych <= 'M') { + if (yych <= 'L') goto yy14; + goto yy27; + } else { + if (yych == 'W') goto yy26; + goto yy14; + } + } +yy31: + YYDEBUG(31, *YYCURSOR); + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); + yych = *YYCURSOR; + YYDEBUG(32, *YYCURSOR); + if (yych <= 'C') { + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy31; + goto yy14; + } else { + if (yych <= 'D') goto yy25; + if (yych == 'W') goto yy26; + goto yy14; + } +yy33: + YYDEBUG(33, *YYCURSOR); + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); + yych = *YYCURSOR; + YYDEBUG(34, *YYCURSOR); + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy33; + if (yych == 'D') goto yy25; + goto yy14; +yy35: + YYDEBUG(35, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= ':') goto yy14; + YYDEBUG(36, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= ':') goto yy14; + YYDEBUG(37, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') { + if (yych == '-') goto yy40; + goto yy14; + } else { + if (yych <= '0') goto yy38; + if (yych <= '1') goto yy39; + goto yy14; + } +yy38: + YYDEBUG(38, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '0') goto yy14; + if (yych <= '9') goto yy61; + goto yy14; +yy39: + YYDEBUG(39, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '2') goto yy61; + goto yy14; +yy40: + YYDEBUG(40, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '0') goto yy41; + if (yych <= '1') goto yy42; + goto yy14; +yy41: + YYDEBUG(41, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '0') goto yy14; + if (yych <= '9') goto yy43; + goto yy14; +yy42: + YYDEBUG(42, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '3') goto yy14; +yy43: + YYDEBUG(43, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '-') goto yy14; + YYDEBUG(44, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '0') goto yy45; + if (yych <= '2') goto yy46; + if (yych <= '3') goto yy47; + goto yy14; +yy45: + YYDEBUG(45, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '0') goto yy14; + if (yych <= '9') goto yy48; + goto yy14; +yy46: + YYDEBUG(46, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy48; + goto yy14; +yy47: + YYDEBUG(47, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '2') goto yy14; +yy48: + YYDEBUG(48, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != 'T') goto yy14; + YYDEBUG(49, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '1') goto yy50; + if (yych <= '2') goto yy51; + goto yy14; +yy50: + YYDEBUG(50, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy52; + goto yy14; +yy51: + YYDEBUG(51, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '5') goto yy14; +yy52: + YYDEBUG(52, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != ':') goto yy14; + YYDEBUG(53, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '6') goto yy14; + YYDEBUG(54, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= ':') goto yy14; + YYDEBUG(55, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != ':') goto yy14; + YYDEBUG(56, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '6') goto yy14; + YYDEBUG(57, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= ':') goto yy14; + YYDEBUG(58, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != 'Z') goto yy14; +yy59: + YYDEBUG(59, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(60, *YYCURSOR); +#line 319 "ext/date/lib/parse_iso_intervals.re" + { + timelib_time *current; + + if (s->have_date || s->have_period) { + current = s->end; + s->have_end_date = 1; + } else { + current = s->begin; + s->have_begin_date = 1; + } + DEBUG_OUTPUT("datetimebasic | datetimeextended"); + TIMELIB_INIT; + current->y = timelib_get_nr((char **) &ptr, 4); + current->m = timelib_get_nr((char **) &ptr, 2); + current->d = timelib_get_nr((char **) &ptr, 2); + current->h = timelib_get_nr((char **) &ptr, 2); + current->i = timelib_get_nr((char **) &ptr, 2); + current->s = timelib_get_nr((char **) &ptr, 2); + s->have_date = 1; + TIMELIB_DEINIT; + return TIMELIB_ISO_DATE; + } +#line 763 "ext/date/lib/parse_iso_intervals.c" +yy61: + YYDEBUG(61, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '0') goto yy62; + if (yych <= '2') goto yy63; + if (yych <= '3') goto yy64; + goto yy14; +yy62: + YYDEBUG(62, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '0') goto yy14; + if (yych <= '9') goto yy65; + goto yy14; +yy63: + YYDEBUG(63, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy65; + goto yy14; +yy64: + YYDEBUG(64, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '2') goto yy14; +yy65: + YYDEBUG(65, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != 'T') goto yy14; + YYDEBUG(66, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '1') goto yy67; + if (yych <= '2') goto yy68; + goto yy14; +yy67: + YYDEBUG(67, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych <= '9') goto yy69; + goto yy14; +yy68: + YYDEBUG(68, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '5') goto yy14; +yy69: + YYDEBUG(69, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '6') goto yy14; + YYDEBUG(70, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= ':') goto yy14; + YYDEBUG(71, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= '6') goto yy14; + YYDEBUG(72, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy14; + if (yych >= ':') goto yy14; + YYDEBUG(73, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'Z') goto yy59; + goto yy14; +yy74: + YYDEBUG(74, *YYCURSOR); + ++YYCURSOR; + if (YYLIMIT == YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + YYDEBUG(75, *YYCURSOR); + if (yych <= '/') goto yy76; + if (yych <= '9') goto yy74; +yy76: + YYDEBUG(76, *YYCURSOR); +#line 308 "ext/date/lib/parse_iso_intervals.re" + { + DEBUG_OUTPUT("recurrences"); + TIMELIB_INIT; + ptr++; + s->recurrences = timelib_get_unsigned_nr((char **) &ptr, 9); + TIMELIB_DEINIT; + s->have_recurrences = 1; + return TIMELIB_PERIOD; + } +#line 851 "ext/date/lib/parse_iso_intervals.c" +} +#line 400 "ext/date/lib/parse_iso_intervals.re" + +} + +#define YYMAXFILL 20 + +void timelib_strtointerval(char *s, int len, + timelib_time **begin, timelib_time **end, + timelib_rel_time **period, int *recurrences, + struct timelib_error_container **errors) +{ + Scanner in; + int t; + char *e = s + len - 1; + + memset(&in, 0, sizeof(in)); + in.errors = malloc(sizeof(struct timelib_error_container)); + in.errors->warning_count = 0; + in.errors->warning_messages = NULL; + in.errors->error_count = 0; + in.errors->error_messages = NULL; + + if (len > 0) { + while (isspace(*s) && s < e) { + s++; + } + while (isspace(*e) && e > s) { + e--; + } + } + if (e - s < 0) { + add_error(&in, "Empty string"); + if (errors) { + *errors = in.errors; + } else { + timelib_error_container_dtor(in.errors); + } + return; + } + e++; + + /* init cursor */ + in.str = malloc((e - s) + YYMAXFILL); + memset(in.str, 0, (e - s) + YYMAXFILL); + memcpy(in.str, s, (e - s)); + in.lim = in.str + (e - s) + YYMAXFILL; + in.cur = in.str; + + /* init value containers */ + in.begin = timelib_time_ctor(); + in.begin->y = TIMELIB_UNSET; + in.begin->d = TIMELIB_UNSET; + in.begin->m = TIMELIB_UNSET; + in.begin->h = TIMELIB_UNSET; + in.begin->i = TIMELIB_UNSET; + in.begin->s = TIMELIB_UNSET; + in.begin->f = TIMELIB_UNSET; + in.begin->z = TIMELIB_UNSET; + in.begin->dst = TIMELIB_UNSET; + in.begin->is_localtime = 0; + in.begin->zone_type = 0; + + in.end = timelib_time_ctor(); + in.end->y = TIMELIB_UNSET; + in.end->d = TIMELIB_UNSET; + in.end->m = TIMELIB_UNSET; + in.end->h = TIMELIB_UNSET; + in.end->i = TIMELIB_UNSET; + in.end->s = TIMELIB_UNSET; + in.end->f = TIMELIB_UNSET; + in.end->z = TIMELIB_UNSET; + in.end->dst = TIMELIB_UNSET; + in.end->is_localtime = 0; + in.end->zone_type = 0; + + in.period = timelib_rel_time_ctor(); + in.period->y = 0; + in.period->d = 0; + in.period->m = 0; + in.period->h = 0; + in.period->i = 0; + in.period->s = 0; + in.period->weekday = 0; + in.period->weekday_behavior = 0; + in.period->first_last_day_of = 0; + + in.recurrences = 1; + + do { + t = scan(&in); +#ifdef DEBUG_PARSER + printf("%d\n", t); +#endif + } while(t != EOI); + + free(in.str); + if (errors) { + *errors = in.errors; + } else { + timelib_error_container_dtor(in.errors); + } + if (in.have_begin_date) { + *begin = in.begin; + } else { + timelib_time_dtor(in.begin); + } + if (in.have_end_date) { + *end = in.end; + } else { + timelib_time_dtor(in.end); + } + if (in.have_period) { + *period = in.period; + } else { + timelib_rel_time_dtor(in.period); + } + if (in.have_recurrences) { + *recurrences = in.recurrences; + } +} + + +/* + * vim: syntax=c + */ diff --git a/ext/date/lib/parse_iso_intervals.re b/ext/date/lib/parse_iso_intervals.re new file mode 100644 index 0000000000..b34bba94be --- /dev/null +++ b/ext/date/lib/parse_iso_intervals.re @@ -0,0 +1,523 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 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: Derick Rethans | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "timelib.h" + +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#else +#include +#endif + +#if defined(_MSC_VER) +# define strtoll(s, f, b) _atoi64(s) +#elif !defined(HAVE_STRTOLL) +# if defined(HAVE_ATOLL) +# define strtoll(s, f, b) atoll(s) +# else +# define strtoll(s, f, b) strtol(s, f, b) +# endif +#endif + +#define TIMELIB_UNSET -99999 + +#define TIMELIB_SECOND 1 +#define TIMELIB_MINUTE 2 +#define TIMELIB_HOUR 3 +#define TIMELIB_DAY 4 +#define TIMELIB_MONTH 5 +#define TIMELIB_YEAR 6 + +#define EOI 257 + +#define TIMELIB_PERIOD 260 +#define TIMELIB_ISO_DATE 261 +#define TIMELIB_ERROR 999 + +typedef unsigned char uchar; + +#define BSIZE 8192 + +#define YYCTYPE uchar +#define YYCURSOR cursor +#define YYLIMIT s->lim +#define YYMARKER s->ptr +#define YYFILL(n) return EOI; + +#define RET(i) {s->cur = cursor; return i;} + +#define timelib_string_free free + +#define TIMELIB_INIT s->cur = cursor; str = timelib_string(s); ptr = str +#define TIMELIB_DEINIT timelib_string_free(str) + +#ifdef DEBUG_PARSER +#define DEBUG_OUTPUT(s) printf("%s\n", s); +#define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } } +#else +#define DEBUG_OUTPUT(s) +#define YYDEBUG(s,c) +#endif + +#include "timelib_structs.h" + +typedef struct Scanner { + int fd; + uchar *lim, *str, *ptr, *cur, *tok, *pos; + unsigned int line, len; + struct timelib_error_container *errors; + + struct timelib_time *begin; + struct timelib_time *end; + struct timelib_rel_time *period; + int recurrences; + + int have_period; + int have_recurrences; + int have_date; + int have_begin_date; + int have_end_date; +} Scanner; + +#define HOUR(a) (int)(a * 60) + +static void add_warning(Scanner *s, char *error) +{ + s->errors->warning_count++; + s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message)); + s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0; + s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0; + s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error); +} + +static void add_error(Scanner *s, char *error) +{ + s->errors->error_count++; + s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message)); + s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0; + s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0; + s->errors->error_messages[s->errors->error_count - 1].message = strdup(error); +} + +static char *timelib_string(Scanner *s) +{ + char *tmp = calloc(1, s->cur - s->tok + 1); + memcpy(tmp, s->tok, s->cur - s->tok); + + return tmp; +} + +static timelib_sll timelib_get_nr(char **ptr, int max_length) +{ + char *begin, *end, *str; + timelib_sll tmp_nr = TIMELIB_UNSET; + int len = 0; + + while ((**ptr < '0') || (**ptr > '9')) { + if (**ptr == '\0') { + return TIMELIB_UNSET; + } + ++*ptr; + } + begin = *ptr; + while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) { + ++*ptr; + ++len; + } + end = *ptr; + str = calloc(1, end - begin + 1); + memcpy(str, begin, end - begin); + tmp_nr = strtoll(str, NULL, 10); + free(str); + return tmp_nr; +} + +static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length) +{ + timelib_ull dir = 1; + + while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { + if (**ptr == '\0') { + return TIMELIB_UNSET; + } + ++*ptr; + } + + while (**ptr == '+' || **ptr == '-') + { + if (**ptr == '-') { + dir *= -1; + } + ++*ptr; + } + return dir * timelib_get_nr(ptr, max_length); +} + +static long timelib_parse_tz_cor(char **ptr) +{ + char *begin = *ptr, *end; + long tmp; + + while (**ptr != '\0') { + ++*ptr; + } + end = *ptr; + switch (end - begin) { + case 1: + case 2: + return HOUR(strtol(begin, NULL, 10)); + break; + case 3: + case 4: + if (begin[1] == ':') { + tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 2, NULL, 10); + return tmp; + } else if (begin[2] == ':') { + tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10); + return tmp; + } else { + tmp = strtol(begin, NULL, 10); + return HOUR(tmp / 100) + tmp % 100; + } + case 5: + tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10); + return tmp; + } + return 0; +} + +static void timelib_eat_spaces(char **ptr) +{ + while (**ptr == ' ' || **ptr == '\t') { + ++*ptr; + } +} + +static void timelib_eat_until_separator(char **ptr) +{ + while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) { + ++*ptr; + } +} + +static long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb) +{ + long retval = 0; + + *tz_not_found = 0; + + while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { + ++*ptr; + } + if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { + *ptr += 3; + } + if (**ptr == '+') { + ++*ptr; + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + *tz_not_found = 0; + t->dst = 0; + + retval = -1 * timelib_parse_tz_cor(ptr); + } else if (**ptr == '-') { + ++*ptr; + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + *tz_not_found = 0; + t->dst = 0; + + retval = timelib_parse_tz_cor(ptr); + } + while (**ptr == ')') { + ++*ptr; + } + return retval; +} + +#define timelib_split_free(arg) { \ + int i; \ + for (i = 0; i < arg.c; i++) { \ + free(arg.v[i]); \ + } \ + if (arg.v) { \ + free(arg.v); \ + } \ +} + +static int scan(Scanner *s) +{ + uchar *cursor = s->cur; + char *str, *ptr = NULL; + +std: + s->tok = cursor; + s->len = 0; +/*!re2c + +/* */ +any = [\000-\377]; +number = [0-9]+; + +hour24lz = [01][0-9] | "2"[0-4]; +minutelz = [0-5][0-9]; +monthlz = "0" [1-9] | "1" [0-2]; +daylz = "0" [1-9] | [1-2][0-9] | "3" [01]; +secondlz = minutelz; +year4 = [0-9]{4}; +weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3]; + +space = [ \t]+; +datetimebasic = year4 monthlz daylz "T" hour24lz minutelz secondlz "Z"; +datetimeextended = year4 "-" monthlz "-" daylz "T" hour24lz ':' minutelz ':' secondlz "Z"; +period = "P" (number "Y")? (number "M")? (number "W")? (number "D")? ("T" (number "H")? (number "M")? (number "S")?)?; + +recurrences = "R" number; + +isoweekday = year4 "-"? "W" weekofyear "-"? [0-7]; +isoweek = year4 "-"? "W" weekofyear; + +*/ + +/*!re2c + /* so that vim highlights correctly */ + recurrences + { + DEBUG_OUTPUT("recurrences"); + TIMELIB_INIT; + ptr++; + s->recurrences = timelib_get_unsigned_nr((char **) &ptr, 9); + TIMELIB_DEINIT; + s->have_recurrences = 1; + return TIMELIB_PERIOD; + } + + datetimebasic| datetimeextended + { + timelib_time *current; + + if (s->have_date || s->have_period) { + current = s->end; + s->have_end_date = 1; + } else { + current = s->begin; + s->have_begin_date = 1; + } + DEBUG_OUTPUT("datetimebasic | datetimeextended"); + TIMELIB_INIT; + current->y = timelib_get_nr((char **) &ptr, 4); + current->m = timelib_get_nr((char **) &ptr, 2); + current->d = timelib_get_nr((char **) &ptr, 2); + current->h = timelib_get_nr((char **) &ptr, 2); + current->i = timelib_get_nr((char **) &ptr, 2); + current->s = timelib_get_nr((char **) &ptr, 2); + s->have_date = 1; + TIMELIB_DEINIT; + return TIMELIB_ISO_DATE; + } + + period + { + timelib_sll nr; + int in_time = 0; + DEBUG_OUTPUT("period"); + TIMELIB_INIT; + ptr++; + do { + if ( *ptr == 'T' ) { + in_time = 1; + ptr++; + } + if ( *ptr == '\0' ) { + add_error(s, "Missing expected time part"); + break; + } + + nr = timelib_get_unsigned_nr((char **) &ptr, 4); + switch (*ptr) { + case 'Y': s->period->y = nr; break; + case 'W': s->period->d = nr * 7; break; + case 'D': s->period->d = nr; break; + case 'H': s->period->h = nr; break; + case 'S': s->period->s = nr; break; + case 'M': + if (in_time) { + s->period->i = nr; + } else { + s->period->m = nr; + } + break; + default: + add_error(s, "Undefined period specifier"); + break; + } + ptr++; + } while (*ptr); + s->have_period = 1; + TIMELIB_DEINIT; + return TIMELIB_PERIOD; + } + + [ .,\t/] + { + goto std; + } + + "\000"|"\n" + { + s->pos = cursor; s->line++; + goto std; + } + + any + { + add_error(s, "Unexpected character"); + goto std; + } +*/ +} + +/*!max:re2c */ + +void timelib_strtointerval(char *s, int len, + timelib_time **begin, timelib_time **end, + timelib_rel_time **period, int *recurrences, + struct timelib_error_container **errors) +{ + Scanner in; + int t; + char *e = s + len - 1; + + memset(&in, 0, sizeof(in)); + in.errors = malloc(sizeof(struct timelib_error_container)); + in.errors->warning_count = 0; + in.errors->warning_messages = NULL; + in.errors->error_count = 0; + in.errors->error_messages = NULL; + + if (len > 0) { + while (isspace(*s) && s < e) { + s++; + } + while (isspace(*e) && e > s) { + e--; + } + } + if (e - s < 0) { + add_error(&in, "Empty string"); + if (errors) { + *errors = in.errors; + } else { + timelib_error_container_dtor(in.errors); + } + return; + } + e++; + + /* init cursor */ + in.str = malloc((e - s) + YYMAXFILL); + memset(in.str, 0, (e - s) + YYMAXFILL); + memcpy(in.str, s, (e - s)); + in.lim = in.str + (e - s) + YYMAXFILL; + in.cur = in.str; + + /* init value containers */ + in.begin = timelib_time_ctor(); + in.begin->y = TIMELIB_UNSET; + in.begin->d = TIMELIB_UNSET; + in.begin->m = TIMELIB_UNSET; + in.begin->h = TIMELIB_UNSET; + in.begin->i = TIMELIB_UNSET; + in.begin->s = TIMELIB_UNSET; + in.begin->f = TIMELIB_UNSET; + in.begin->z = TIMELIB_UNSET; + in.begin->dst = TIMELIB_UNSET; + in.begin->is_localtime = 0; + in.begin->zone_type = 0; + + in.end = timelib_time_ctor(); + in.end->y = TIMELIB_UNSET; + in.end->d = TIMELIB_UNSET; + in.end->m = TIMELIB_UNSET; + in.end->h = TIMELIB_UNSET; + in.end->i = TIMELIB_UNSET; + in.end->s = TIMELIB_UNSET; + in.end->f = TIMELIB_UNSET; + in.end->z = TIMELIB_UNSET; + in.end->dst = TIMELIB_UNSET; + in.end->is_localtime = 0; + in.end->zone_type = 0; + + in.period = timelib_rel_time_ctor(); + in.period->y = 0; + in.period->d = 0; + in.period->m = 0; + in.period->h = 0; + in.period->i = 0; + in.period->s = 0; + in.period->weekday = 0; + in.period->weekday_behavior = 0; + in.period->first_last_day_of = 0; + + in.recurrences = 1; + + do { + t = scan(&in); +#ifdef DEBUG_PARSER + printf("%d\n", t); +#endif + } while(t != EOI); + + free(in.str); + if (errors) { + *errors = in.errors; + } else { + timelib_error_container_dtor(in.errors); + } + if (in.have_begin_date) { + *begin = in.begin; + } else { + timelib_time_dtor(in.begin); + } + if (in.have_end_date) { + *end = in.end; + } else { + timelib_time_dtor(in.end); + } + if (in.have_period) { + *period = in.period; + } else { + timelib_rel_time_dtor(in.period); + } + if (in.have_recurrences) { + *recurrences = in.recurrences; + } +} + + +/* + * vim: syntax=c + */ diff --git a/ext/date/lib/timelib.c b/ext/date/lib/timelib.c index c6e019abac..064237ffb3 100644 --- a/ext/date/lib/timelib.c +++ b/ext/date/lib/timelib.c @@ -38,6 +38,14 @@ timelib_time* timelib_time_ctor(void) return t; } +timelib_rel_time* timelib_rel_time_ctor(void) +{ + timelib_rel_time *t; + t = calloc(1, sizeof(timelib_rel_time)); + + return t; +} + void timelib_time_tz_abbr_update(timelib_time* tm, char* tz_abbr) { unsigned int i; @@ -55,6 +63,11 @@ void timelib_time_dtor(timelib_time* t) TIMELIB_TIME_FREE(t); } +void timelib_rel_time_dtor(timelib_rel_time* t) +{ + TIMELIB_TIME_FREE(t); +} + timelib_time_offset* timelib_time_offset_ctor(void) { timelib_time_offset *t; @@ -114,6 +127,7 @@ void timelib_tzinfo_dtor(timelib_tzinfo *tz) TIMELIB_TIME_FREE(tz->timezone_abbr); TIMELIB_TIME_FREE(tz->leap_times); TIMELIB_TIME_FREE(tz); + tz = NULL; } char *timelib_get_tz_abbr_ptr(timelib_time *t) @@ -228,3 +242,20 @@ void timelib_dump_date(timelib_time *d, int options) printf("\n"); } +void timelib_dump_rel_time(timelib_rel_time *d) +{ + printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS (days: %lld)%s", + d->y, d->m, d->d, d->h, d->i, d->s, d->days, d->invert ? " inverted" : ""); + if (d->first_last_day_of != 0) { + switch (d->first_last_day_of) { + case 1: + printf(" / first day of"); + break; + case 2: + printf(" / last day of"); + break; + } + } + printf("\n"); +} + diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index 1960a63de9..75516dd3c7 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -62,8 +62,16 @@ void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options); char *timelib_timezone_id_from_abbr(const char *abbr, long gmtoffset, int isdst); const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void); +/* From parse_iso_intervals.re */ +void timelib_strtointerval(char *s, int len, + timelib_time **begin, timelib_time **end, + timelib_rel_time **period, int *recurrences, + struct timelib_error_container **errors); + + /* From tm2unixtime.c */ void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi); +void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt); /* From unixtime2tm.c */ int timelib_apply_localtime(timelib_time *t, unsigned int localtime); @@ -89,6 +97,9 @@ void timelib_time_tz_name_update(timelib_time* tm, char* tz_name); void timelib_tzinfo_dtor(timelib_tzinfo *tz); timelib_tzinfo* timelib_tzinfo_clone(timelib_tzinfo *tz); +timelib_rel_time* timelib_rel_time_ctor(void); +void timelib_rel_time_dtor(timelib_rel_time* t); + timelib_time* timelib_time_ctor(void); void timelib_time_set_option(timelib_time* tm, int option, void* option_value); void timelib_time_dtor(timelib_time* t); @@ -100,6 +111,7 @@ void timelib_error_container_dtor(timelib_error_container *errors); signed long timelib_date_to_int(timelib_time *d, int *error); void timelib_dump_date(timelib_time *d, int options); +void timelib_dump_rel_time(timelib_rel_time *d); void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec); @@ -107,4 +119,7 @@ void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec); double timelib_ts_to_juliandate(timelib_sll ts); int timelib_astro_rise_set_altitude(timelib_time *time, double lon, double lat, double altit, int upper_limb, double *h_rise, double *h_set, timelib_sll *ts_rise, timelib_sll *ts_set, timelib_sll *ts_transit); +/* from interval.c */ +timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two); + #endif diff --git a/ext/date/lib/timelib_structs.h b/ext/date/lib/timelib_structs.h index 4d6ed12a7f..1aeabd4712 100644 --- a/ext/date/lib/timelib_structs.h +++ b/ext/date/lib/timelib_structs.h @@ -124,6 +124,8 @@ typedef struct timelib_rel_time { int weekday_behavior; /* 0: the current day should *not* be counted when advancing forwards; 1: the current day *should* be counted */ int first_last_day_of; + int invert; /* Whether the difference should be inverted */ + timelib_sll days; /* Contains the number of *days*, instead of Y-M-D differences */ } timelib_rel_time; typedef struct timelib_time_offset { diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c index c1e75159f8..7464968676 100644 --- a/ext/date/lib/tm2unixtime.c +++ b/ext/date/lib/tm2unixtime.c @@ -49,6 +49,41 @@ static int do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, t return 0; } +static int do_range_limit_days_relative(timelib_sll *base_y, timelib_sll *base_m, timelib_sll *y, timelib_sll *m, timelib_sll *d) +{ + timelib_sll leapyear; + timelib_sll days_this_month; + timelib_sll next_month, next_year; + timelib_sll days_next_month; + + do_range_limit(1, 13, 12, base_m, base_y); + + leapyear = timelib_is_leap(*base_y); + days_this_month = leapyear ? days_in_month_leap[*base_m] : days_in_month[*base_m]; + next_month = (*base_m) + 1; + + if (next_month > 12) { + next_month -= 12; + next_year = (*base_y) + 1; + } else { + next_year = (*base_y); + } + leapyear = timelib_is_leap(next_year); + days_next_month = leapyear ? days_in_month_leap[next_month] : days_in_month[next_month]; + + if (*d < 0) { + *d += days_this_month; + (*m)--; + return 1; + } + if (*d > days_next_month) { + *d -= days_next_month; + (*m)++; + return 1; + } + return 0; +} + static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d) { timelib_sll leapyear; @@ -101,6 +136,17 @@ static void do_adjust_for_weekday(timelib_time* time) time->have_weekday_relative = 0; } +void timelib_do_rel_normalize(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 {} while (do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d)); + do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y)); +} + static void do_normalize(timelib_time* time) { do {} while (do_range_limit(0, 60, 60, &time->s, &time->i)); @@ -293,7 +339,20 @@ static timelib_sll do_adjust_timezone(timelib_time *tz, timelib_tzinfo *tzi) } timelib_time_offset_dtor(before); timelib_time_offset_dtor(after); - + + { + timelib_time_offset *gmt_offset; + + gmt_offset = timelib_get_time_zone_info(tz->sse + tmp, tzi); + tz->z = gmt_offset->offset; + + tz->dst = gmt_offset->is_dst; + if (tz->tz_abbr) { + free(tz->tz_abbr); + } + tz->tz_abbr = strdup(gmt_offset->abbr); + timelib_time_offset_dtor(gmt_offset); + } return tmp; } } diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 2ef63f8a0e..4bf73d8881 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -174,9 +174,12 @@ const zend_function_entry date_functions[] = { PHP_FE(date_get_last_errors, NULL) PHP_FE(date_format, NULL) PHP_FE(date_modify, NULL) + PHP_FE(date_add, NULL) + PHP_FE(date_sub, NULL) PHP_FE(date_timezone_get, NULL) PHP_FE(date_timezone_set, NULL) PHP_FE(date_offset_get, NULL) + PHP_FE(date_diff, NULL) PHP_FE(date_time_set, NULL) PHP_FE(date_date_set, NULL) @@ -192,6 +195,8 @@ const zend_function_entry date_functions[] = { PHP_FE(timezone_identifiers_list, NULL) PHP_FE(timezone_abbreviations_list, NULL) + PHP_FE(date_interval_format, NULL) + /* Options and Configuration */ PHP_FE(date_default_timezone_set, arginfo_date_default_timezone_set) PHP_FE(date_default_timezone_get, arginfo_date_default_timezone_get) @@ -211,6 +216,8 @@ const zend_function_entry date_funcs_date[] = { PHP_ME_MAPPING(getLastErrors, date_get_last_errors, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME_MAPPING(format, date_format, NULL, 0) PHP_ME_MAPPING(modify, date_modify, NULL, 0) + PHP_ME_MAPPING(add, date_add, NULL, 0) + PHP_ME_MAPPING(sub, date_sub, NULL, 0) PHP_ME_MAPPING(getTimezone, date_timezone_get, NULL, 0) PHP_ME_MAPPING(setTimezone, date_timezone_set, NULL, 0) PHP_ME_MAPPING(getOffset, date_offset_get, NULL, 0) @@ -219,6 +226,7 @@ const zend_function_entry date_funcs_date[] = { PHP_ME_MAPPING(setISODate, date_isodate_set, NULL, 0) PHP_ME_MAPPING(setTimestamp, date_timestamp_set, NULL, 0) PHP_ME_MAPPING(getTimestamp, date_timestamp_get, NULL, 0) + PHP_ME_MAPPING(diff, date_diff, NULL, 0) {NULL, NULL, NULL} }; @@ -232,9 +240,14 @@ const zend_function_entry date_funcs_timezone[] = { {NULL, NULL, NULL} }; +const zend_function_entry date_funcs_interval[] = { + PHP_ME(DateInterval, __construct, NULL, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) + PHP_ME_MAPPING(format, date_interval_format, NULL, 0) + {NULL, NULL, NULL} +}; + static char* guess_timezone(const timelib_tzdb *tzdb TSRMLS_DC); static void date_register_classes(TSRMLS_D); -static char* guess_timezone(const timelib_tzdb *tzdb TSRMLS_DC); /* }}} */ ZEND_DECLARE_MODULE_GLOBALS(date) @@ -263,13 +276,15 @@ PHP_INI_BEGIN() PHP_INI_END() /* }}} */ -zend_class_entry *date_ce_date, *date_ce_timezone; +zend_class_entry *date_ce_date, *date_ce_timezone, *date_ce_interval; static zend_object_handlers date_object_handlers_date; static zend_object_handlers date_object_handlers_timezone; +static zend_object_handlers date_object_handlers_interval; typedef struct _php_date_obj php_date_obj; typedef struct _php_timezone_obj php_timezone_obj; +typedef struct _php_interval_obj php_interval_obj; struct _php_date_obj { zend_object std; @@ -293,6 +308,13 @@ struct _php_timezone_obj { } tzi; }; +struct _php_interval_obj { + zend_object std; + timelib_rel_time *diff; + HashTable *props; + int initialized; +}; + #define DATE_SET_CONTEXT \ zval *object; \ object = getThis(); \ @@ -319,12 +341,21 @@ struct _php_timezone_obj { static void date_object_free_storage_date(void *object TSRMLS_DC); static void date_object_free_storage_timezone(void *object TSRMLS_DC); +static void date_object_free_storage_interval(void *object TSRMLS_DC); + static zend_object_value date_object_new_date(zend_class_entry *class_type TSRMLS_DC); static zend_object_value date_object_new_timezone(zend_class_entry *class_type TSRMLS_DC); +static zend_object_value date_object_new_interval(zend_class_entry *class_type TSRMLS_DC); + static zend_object_value date_object_clone_date(zval *this_ptr TSRMLS_DC); +static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC); +static zend_object_value date_object_clone_interval(zval *this_ptr TSRMLS_DC); + static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC); static HashTable *date_object_get_properties(zval *object TSRMLS_DC); -static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC); + +zval *date_interval_read_property(zval *object, zval *member, int type TSRMLS_DC); +void date_interval_write_property(zval *object, zval *member, zval *value TSRMLS_DC); /* This is need to ensure that session extension request shutdown occurs 1st, because it uses the date extension */ static const zend_module_dep date_deps[] = { @@ -1520,7 +1551,7 @@ PHP_FUNCTION(getdate) static void date_register_classes(TSRMLS_D) { - zend_class_entry ce_date, ce_timezone; + zend_class_entry ce_date, ce_timezone, ce_interval; INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date); ce_date.create_object = date_object_new_date; @@ -1568,6 +1599,14 @@ static void date_register_classes(TSRMLS_D) REGISTER_TIMEZONE_CLASS_CONST_STRING("UTC", PHP_DATE_TIMEZONE_GROUP_UTC); REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL", PHP_DATE_TIMEZONE_GROUP_ALL); REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL_WITH_BC", PHP_DATE_TIMEZONE_GROUP_ALL_W_BC); + + INIT_CLASS_ENTRY(ce_interval, "DateInterval", date_funcs_interval); + ce_interval.create_object = date_object_new_interval; + date_ce_interval = zend_register_internal_class_ex(&ce_interval, NULL, NULL TSRMLS_CC); + memcpy(&date_object_handlers_interval, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + date_object_handlers_interval.clone_obj = date_object_clone_interval; + date_object_handlers_interval.read_property = date_interval_read_property; + date_object_handlers_interval.write_property = date_interval_write_property; } static inline zend_object_value date_object_new_date_ex(zend_class_entry *class_type, php_date_obj **ptr TSRMLS_DC) @@ -1743,6 +1782,44 @@ static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC) return new_ov; } +static inline zend_object_value date_object_new_interval_ex(zend_class_entry *class_type, php_interval_obj **ptr TSRMLS_DC) +{ + php_interval_obj *intern; + zend_object_value retval; + zval *tmp; + + intern = emalloc(sizeof(php_interval_obj)); + memset(intern, 0, sizeof(php_interval_obj)); + if (ptr) { + *ptr = intern; + } + + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + + retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) date_object_free_storage_interval, NULL TSRMLS_CC); + retval.handlers = &date_object_handlers_interval; + + return retval; +} + +static zend_object_value date_object_new_interval(zend_class_entry *class_type TSRMLS_DC) +{ + return date_object_new_interval_ex(class_type, NULL TSRMLS_CC); +} + +static zend_object_value date_object_clone_interval(zval *this_ptr TSRMLS_DC) +{ + php_interval_obj *new_obj = NULL; + php_interval_obj *old_obj = (php_interval_obj *) zend_object_store_get_object(this_ptr TSRMLS_CC); + zend_object_value new_ov = date_object_new_interval_ex(old_obj->std.ce, &new_obj TSRMLS_CC); + + zend_objects_clone_members(&new_obj->std, new_ov, &old_obj->std, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); + + /** FIX ME ADD CLONE STUFF **/ + return new_ov; +} + static void date_object_free_storage_date(void *object TSRMLS_DC) { php_date_obj *intern = (php_date_obj *)object; @@ -1769,6 +1846,15 @@ static void date_object_free_storage_timezone(void *object TSRMLS_DC) efree(object); } +static void date_object_free_storage_interval(void *object TSRMLS_DC) +{ + php_interval_obj *intern = (php_interval_obj *)object; + + timelib_rel_time_dtor(intern->diff); + zend_object_std_dtor(&intern->std TSRMLS_CC); + efree(object); +} + /* Advanced Interface */ static zval * date_instantiate(zend_class_entry *pce, zval *object TSRMLS_DC) { @@ -2233,6 +2319,82 @@ PHP_FUNCTION(date_modify) } /* }}} */ +/* {{{ proto void date_add(DateTime object, DateInterval interval) + Adds an interval to the current date in object. +*/ +PHP_FUNCTION(date_add) +{ + zval *object, *interval; + php_date_obj *dateobj; + php_interval_obj *intobj; + int bias = 1; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) { + RETURN_FALSE; + } + dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); + DATE_CHECK_INITIALIZED(dateobj->time, DateTime); + intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC); + DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval); + + if (intobj->diff->invert) { + bias = -1; + } + + dateobj->time->relative.y = intobj->diff->y * bias; + dateobj->time->relative.m = intobj->diff->m * bias; + dateobj->time->relative.d = intobj->diff->d * bias; + dateobj->time->relative.h = intobj->diff->h * bias; + dateobj->time->relative.i = intobj->diff->i * bias; + dateobj->time->relative.s = intobj->diff->s * bias; + dateobj->time->relative.weekday = 0; + dateobj->time->have_relative = 1; + dateobj->time->have_weekday_relative = 0; + dateobj->time->sse_uptodate = 0; + + timelib_update_ts(dateobj->time, NULL); + timelib_update_from_sse(dateobj->time); +} +/* }}} */ + +/* {{{ proto void date_sub(DateTime object, DateInterval interval) + Subtracts an interval to the current date in object. +*/ +PHP_FUNCTION(date_sub) +{ + zval *object, *interval; + php_date_obj *dateobj; + php_interval_obj *intobj; + int bias = 1; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) { + RETURN_FALSE; + } + dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); + DATE_CHECK_INITIALIZED(dateobj->time, DateTime); + intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC); + DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval); + + if (intobj->diff->invert) { + bias = -1; + } + + dateobj->time->relative.y = 0 - (intobj->diff->y * bias); + dateobj->time->relative.m = 0 - (intobj->diff->m * bias); + dateobj->time->relative.d = 0 - (intobj->diff->d * bias); + dateobj->time->relative.h = 0 - (intobj->diff->h * bias); + dateobj->time->relative.i = 0 - (intobj->diff->i * bias); + dateobj->time->relative.s = 0 - (intobj->diff->s * bias); + dateobj->time->relative.weekday = 0; + dateobj->time->have_relative = 1; + dateobj->time->have_weekday_relative = 0; + dateobj->time->sse_uptodate = 0; + + timelib_update_ts(dateobj->time, NULL); + timelib_update_from_sse(dateobj->time); +} +/* }}} */ + /* {{{ proto DateTimeZone date_timezone_get(DateTime object) Return new DateTimeZone object relative to give DateTime */ @@ -2444,6 +2606,36 @@ PHP_FUNCTION(date_timestamp_get) } /* }}} */ +/* {{{ proto DateInterval date_diff(DateTime object [, bool absolute]) + Returns the difference between two DateTime objects. +*/ +PHP_FUNCTION(date_diff) +{ + zval *object1, *object2; + php_date_obj *dateobj1, *dateobj2; + php_interval_obj *interval; + long absolute = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO|l", &object1, date_ce_date, &object2, date_ce_date, &absolute) == FAILURE) { + RETURN_FALSE; + } + dateobj1 = (php_date_obj *) zend_object_store_get_object(object1 TSRMLS_CC); + dateobj2 = (php_date_obj *) zend_object_store_get_object(object2 TSRMLS_CC); + DATE_CHECK_INITIALIZED(dateobj1->time, DateTime); + DATE_CHECK_INITIALIZED(dateobj2->time, DateTime); + timelib_update_ts(dateobj1->time, NULL); + timelib_update_ts(dateobj2->time, NULL); + + date_instantiate(date_ce_interval, return_value TSRMLS_CC); + interval = zend_object_store_get_object(return_value TSRMLS_CC); + interval->diff = timelib_diff(dateobj1->time, dateobj2->time); + if (absolute) { + interval->diff->invert = 0; + } + interval->initialized = 1; +} +/* }}} */ + static int timezone_initialize(timelib_tzinfo **tzi, /*const*/ char *tz TSRMLS_DC) { char *tzid; @@ -2464,6 +2656,119 @@ static int timezone_initialize(timelib_tzinfo **tzi, /*const*/ char *tz TSRMLS_D } } +static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, int format_length TSRMLS_DC) +{ + timelib_time *b = NULL, *e = NULL; + timelib_rel_time *p = NULL; + int r = 0; + int retval = 0; + struct timelib_error_container *errors; + + timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors); + + if (errors->error_count > 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad format (%s)", format); + retval = FAILURE; + } else { + *rt = p; + retval = SUCCESS; + } + timelib_error_container_dtor(errors); + return retval; +} + +/* {{{ date_interval_read_property */ +zval *date_interval_read_property(zval *object, zval *member, int type TSRMLS_DC) +{ + php_interval_obj *obj; + zval *retval; + zval tmp_member; + timelib_sll value = -1; + + if (member->type != IS_STRING) { + tmp_member = *member; + zval_copy_ctor(&tmp_member); + convert_to_string(&tmp_member); + member = &tmp_member; + } + + obj = (php_interval_obj *)zend_objects_get_address(object TSRMLS_CC); + +#define GET_VALUE_FROM_STRUCT(n,m) \ + if (strcmp(Z_STRVAL_P(member), m) == 0) { \ + value = obj->diff->n; \ + } + GET_VALUE_FROM_STRUCT(y, "y"); + GET_VALUE_FROM_STRUCT(m, "m"); + GET_VALUE_FROM_STRUCT(d, "d"); + GET_VALUE_FROM_STRUCT(h, "h"); + GET_VALUE_FROM_STRUCT(i, "i"); + GET_VALUE_FROM_STRUCT(s, "s"); + GET_VALUE_FROM_STRUCT(invert, "invert"); + GET_VALUE_FROM_STRUCT(days, "days"); + + if (value == -1) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unknown property (%s)", Z_STRVAL_P(member)); + } + + ALLOC_INIT_ZVAL(retval); + ZVAL_LONG(retval, value); + + if (member == &tmp_member) { + zval_dtor(member); + } + + return retval; +} +/* }}} */ + +/* {{{ date_interval_write_property */ +void date_interval_write_property(zval *object, zval *member, zval *value TSRMLS_DC) +{ + php_interval_obj *obj; + zval tmp_member, tmp_value; + + if (member->type != IS_STRING) { + tmp_member = *member; + zval_copy_ctor(&tmp_member); + convert_to_string(&tmp_member); + member = &tmp_member; + } + obj = (php_interval_obj *)zend_objects_get_address(object TSRMLS_CC); + +#define SET_VALUE_FROM_STRUCT(n,m) \ + if (strcmp(Z_STRVAL_P(member), m) == 0) { \ + if (value->type != IS_LONG) { \ + tmp_value = *value; \ + zval_copy_ctor(&tmp_value); \ + convert_to_long(&tmp_value); \ + value = &tmp_value; \ + } \ + obj->diff->n = Z_LVAL_P(value); \ + if (value == &tmp_value) { \ + zval_dtor(value); \ + } \ + } + + SET_VALUE_FROM_STRUCT(y, "y"); + SET_VALUE_FROM_STRUCT(m, "m"); + SET_VALUE_FROM_STRUCT(d, "d"); + SET_VALUE_FROM_STRUCT(h, "h"); + SET_VALUE_FROM_STRUCT(i, "i"); + SET_VALUE_FROM_STRUCT(s, "s"); + SET_VALUE_FROM_STRUCT(invert, "invert"); + + if (value == -1) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unknown property (%s)", Z_STRVAL_P(member)); + } + + if (member == &tmp_member) { + zval_dtor(member); + } +} +/* }}} */ + + /* {{{ proto DateTimeZone timezone_open(string timezone) Returns new DateTimeZone object */ @@ -2591,9 +2896,19 @@ PHP_FUNCTION(timezone_offset_get) dateobj = (php_date_obj *) zend_object_store_get_object(dateobject TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); - offset = timelib_get_time_zone_info(dateobj->time->sse, tzobj->tzi.tz); - RETVAL_LONG(offset->offset); - timelib_time_offset_dtor(offset); + switch (tzobj->type) { + case TIMELIB_ZONETYPE_ID: + offset = timelib_get_time_zone_info(dateobj->time->sse, tzobj->tzi.tz); + RETVAL_LONG(offset->offset); + timelib_time_offset_dtor(offset); + break; + case TIMELIB_ZONETYPE_OFFSET: + RETURN_LONG(tzobj->tzi.utc_offset * -60); + break; + case TIMELIB_ZONETYPE_ABBR: + RETURN_LONG((tzobj->tzi.z.utc_offset - (tzobj->tzi.z.dst*60)) * -60); + break; + } } /* }}} */ @@ -2679,6 +2994,105 @@ PHP_FUNCTION(timezone_transitions_get) } /* }}} */ +/* {{{ proto DateInterval::__construct([string interval_spec]) + Creates new DateInterval object. +*/ +PHP_METHOD(DateInterval, __construct) +{ + char *interval_string = NULL; + int interval_string_length; + php_interval_obj *diobj; + timelib_rel_time *reltime; + + php_set_error_handling(EH_THROW, NULL TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &interval_string, &interval_string_length) == SUCCESS) { + if (date_interval_initialize(&reltime, interval_string, interval_string_length TSRMLS_CC) == SUCCESS) { + diobj = zend_object_store_get_object(getThis() TSRMLS_CC); + diobj->diff = reltime; + diobj->initialized = 1; + } else { + ZVAL_NULL(getThis()); + } + } + php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC); +} +/* }}} */ + +/* {{{ date_interval_format - */ +static char *date_interval_format(char *format, int format_len, timelib_rel_time *t) +{ + smart_str string = {0}; + int i, length, have_format_spec = 0; + char buffer[33]; + + if (!format_len) { + return estrdup(""); + } + + for (i = 0; i < format_len; i++) { + if (have_format_spec) { + switch (format[i]) { + case 'Y': length = slprintf(buffer, 32, "%02d", (int) t->y); break; + case 'y': length = slprintf(buffer, 32, "%d", (int) t->y); break; + + case 'M': length = slprintf(buffer, 32, "%02d", (int) t->m); break; + case 'm': length = slprintf(buffer, 32, "%d", (int) t->m); break; + + case 'D': length = slprintf(buffer, 32, "%02d", (int) t->d); break; + case 'd': length = slprintf(buffer, 32, "%d", (int) t->d); break; + + case 'H': length = slprintf(buffer, 32, "%02d", (int) t->h); break; + case 'h': length = slprintf(buffer, 32, "%d", (int) t->h); break; + + case 'I': length = slprintf(buffer, 32, "%02d", (int) t->i); break; + case 'i': length = slprintf(buffer, 32, "%d", (int) t->i); break; + + case 'S': length = slprintf(buffer, 32, "%02d", (int) t->s); break; + case 's': length = slprintf(buffer, 32, "%d", (int) t->s); break; + + case 'a': length = slprintf(buffer, 32, "%d", (int) t->days); break; + case 'r': length = slprintf(buffer, 32, "%s", t->invert ? "-" : ""); break; + case 'R': length = slprintf(buffer, 32, "%c", t->invert ? '-' : '+'); break; + + case '%': length = slprintf(buffer, 32, "%%"); break; + default: buffer[0] = '%'; buffer[1] = format[i]; buffer[2] = '\0'; length = 2; break; + } + smart_str_appendl(&string, buffer, length); + have_format_spec = 0; + } else { + if (format[i] == '%') { + have_format_spec = 1; + } else { + smart_str_appendc(&string, format[i]); + } + } + } + + smart_str_0(&string); + + return string.c; +} +/* }}} */ + +/* {{{ proto string date_interval_format(DateInterval object) + Formats the interval. +*/ +PHP_FUNCTION(date_interval_format) +{ + zval *object; + php_interval_obj *diobj; + char *format; + int format_len; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_interval, &format, &format_len) == FAILURE) { + RETURN_FALSE; + } + diobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC); + DATE_CHECK_INITIALIZED(diobj->initialized, DateInterval); + + RETURN_STRING(date_interval_format(format, format_len, diobj->diff), 0); +} +/* }}} */ static int check_id_allowed(char *id, long what) { if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA && strncasecmp(id, "Africa/", 7) == 0) return 1; diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 06924865f5..59ffabdec1 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -57,9 +57,12 @@ PHP_FUNCTION(date_parse_from_format); PHP_FUNCTION(date_get_last_errors); PHP_FUNCTION(date_format); PHP_FUNCTION(date_modify); +PHP_FUNCTION(date_add); +PHP_FUNCTION(date_sub); PHP_FUNCTION(date_timezone_get); PHP_FUNCTION(date_timezone_set); PHP_FUNCTION(date_offset_get); +PHP_FUNCTION(date_diff); PHP_FUNCTION(date_time_set); PHP_FUNCTION(date_date_set); @@ -76,6 +79,9 @@ PHP_FUNCTION(timezone_transitions_get); PHP_FUNCTION(timezone_identifiers_list); PHP_FUNCTION(timezone_abbreviations_list); +PHP_METHOD(DateInterval, __construct); +PHP_FUNCTION(date_interval_format); + /* Options and Configuration */ PHP_FUNCTION(date_default_timezone_set); PHP_FUNCTION(date_default_timezone_get); diff --git a/ext/date/tests/bug44742.phpt b/ext/date/tests/bug44742.phpt new file mode 100644 index 0000000000..e6df1efabd --- /dev/null +++ b/ext/date/tests/bug44742.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug #44742 (timezone_offset_get() causes segmentation faults) +--FILE-- + +--EXPECT-- +int(0) +int(7200) +int(12600) +int(-18000) +int(-41400) +int(7200) +int(3600) +int(0) +int(-14400) +int(7200) +int(28800)