]> granicus.if.org Git - php/commitdiff
- Added new date/time functionality:
authorDerick Rethans <derick@php.net>
Fri, 25 Apr 2008 12:35:58 +0000 (12:35 +0000)
committerDerick Rethans <derick@php.net>
Fri, 25 Apr 2008 12:35:58 +0000 (12:35 +0000)
  . 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).

14 files changed:
NEWS
ext/date/config.m4
ext/date/lib/README
ext/date/lib/interval.c [new file with mode: 0644]
ext/date/lib/parse_date.c
ext/date/lib/parse_iso_intervals.c [new file with mode: 0644]
ext/date/lib/parse_iso_intervals.re [new file with mode: 0644]
ext/date/lib/timelib.c
ext/date/lib/timelib.h
ext/date/lib/timelib_structs.h
ext/date/lib/tm2unixtime.c
ext/date/php_date.c
ext/date/php_date.h
ext/date/tests/bug44742.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 5027353f7f7b4961d1573d4ff48f30742f1d9db6..aba6cce0ff73d27088b37e226e496192c0b4c397 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -49,6 +49,11 @@ PHP                                                                        NEWS
   . support for "first/last day of <month>" 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)
index cbd732f4edfbc5f5fe12dd5a35a0c2d9090272e9..f403104a8afbeae26eb156531acc68c87a255c3b 100644 (file)
@@ -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)
 
index 12f09bc5e28284501f56c0c8af5ebf7939d6849c..2b6b7f2c4309703216d1044374c14cede83539bd 100644 (file)
@@ -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 (file)
index 0000000..b7c94af
--- /dev/null
@@ -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 <derick@derickrethans.nl>                    |
+   +----------------------------------------------------------------------+
+ */
+
+/* $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;
+}
index 0fd4985014e838fb066107ff0029e3bdd1d2eb51..3762a6dec98caae55edf1227bf925f59e3bf7f4f 100644 (file)
@@ -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 (file)
index 0000000..067f547
--- /dev/null
@@ -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 <derick@derickrethans.nl>                    |
+   +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include "timelib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#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 (file)
index 0000000..b34bba9
--- /dev/null
@@ -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 <derick@derickrethans.nl>                    |
+   +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include "timelib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#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
+ */
index c6e019abac0b083787f5bfbeee8b2679a238e766..064237ffb3734b3355fa218acaf4f65e2becc3b3 100644 (file)
@@ -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");
+}
+
index 1960a63de9cfd5211b5b99de756f7b470f94b9e8..75516dd3c734f18f652163b677fd78b120131ddb 100644 (file)
@@ -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
index 4d6ed12a7fb3341c276e8ed3d2ba397ad577289f..1aeabd471297bdee5283b4a4c10c2d4ba8023f8c 100644 (file)
@@ -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 {
index c1e75159f886e935df5fa34582716bb6b218fea1..7464968676b8ba00d7a1e0a0b801529ffcee5f7b 100644 (file)
@@ -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;
                        }
        }
index 2ef63f8a0e0f0c0da5d71f8892b5f2b74e6923a4..4bf73d8881e4fa9d7df0762e0c1bee18f3dfc080 100644 (file)
@@ -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;
index 06924865f52122d47a8b733f5c515cbf0f9791b2..59ffabdec133409519d0ff9b5a5a274b6fc3d03f 100644 (file)
@@ -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 (file)
index 0000000..e6df1ef
--- /dev/null
@@ -0,0 +1,35 @@
+--TEST--
+Bug #44742 (timezone_offset_get() causes segmentation faults)
+--FILE--
+<?php
+$dates = array(
+       "2008-04-11 00:00:00+0000",
+       "2008-04-11 00:00:00+0200",
+       "2008-04-11 00:00:00+0330",
+       "2008-04-11 00:00:00-0500",
+       "2008-04-11 00:00:00-1130",
+       "2008-04-11 00:00:00 CEST",
+       "2008-04-11 00:00:00 CET",
+       "2008-04-11 00:00:00 UTC",
+       "2008-04-11 00:00:00 America/New_York",
+       "2008-04-11 00:00:00 Europe/Oslo",
+       "2008-04-11 00:00:00 Asia/Singapore",
+);
+foreach ($dates as $date)
+{
+       $date = date_create($date);
+       var_dump(timezone_offset_get(date_timezone_get($date), $date));
+}
+?>
+--EXPECT--
+int(0)
+int(7200)
+int(12600)
+int(-18000)
+int(-41400)
+int(7200)
+int(3600)
+int(0)
+int(-14400)
+int(7200)
+int(28800)