summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerick Rethans <derick@php.net>2009-05-03 18:22:40 +0000
committerDerick Rethans <derick@php.net>2009-05-03 18:22:40 +0000
commitfebc2763dd872d484ed7df09f778a0a85104102a (patch)
tree8a8b86fea5dd273a9b8901f96e2a17d09c5d8059
parent7e0387dbde7f0d3da6c948b29f036dedb4bbc248 (diff)
downloadphp-git-febc2763dd872d484ed7df09f778a0a85104102a.tar.gz
- MFH: Fixed bug #48058 (Year formatter goes wrong with out-of-int range).
- MFH: Fixed bug #45822 (Near infinite-loops while parsing huge relative offsets).
-rw-r--r--ext/date/lib/timelib_structs.h3
-rw-r--r--ext/date/lib/tm2unixtime.c22
-rw-r--r--ext/date/php_date.c2
-rw-r--r--ext/date/tests/bug48058.phpt29
-rw-r--r--ext/date/tests/date_create-relative.phpt93
-rw-r--r--ext/date/tests/strtotime-relative.phpt98
6 files changed, 234 insertions, 13 deletions
diff --git a/ext/date/lib/timelib_structs.h b/ext/date/lib/timelib_structs.h
index 3deecd0645..17e66835ff 100644
--- a/ext/date/lib/timelib_structs.h
+++ b/ext/date/lib/timelib_structs.h
@@ -216,6 +216,9 @@ typedef struct _timelib_tzdb {
#define SECS_PER_DAY 86400
#define DAYS_PER_YEAR 365
#define DAYS_PER_LYEAR 366
+/* 400*365 days + 97 leap days */
+#define DAYS_PER_LYEAR_PERIOD 146097
+#define YEARS_PER_LYEAR_PERIOD 400
#define timelib_is_leap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c
index 6d0684b018..6b737141fb 100644
--- a/ext/date/lib/tm2unixtime.c
+++ b/ext/date/lib/tm2unixtime.c
@@ -31,20 +31,12 @@ static int days_in_month[13] = { 31, 31, 28, 31, 30, 31, 30, 31, 3
static int do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
{
if (*a < start) {
- *a += adj;
- (*b)--;
- return 1;
+ *b -= (start - *a - 1) / adj + 1;
+ *a += adj * ((start - *a - 1) / adj + 1);
}
if (*a >= end) {
- if (start == 0) {
- (*b) += (*a / end);
- (*a) -= (end * (*a / end));
- return 0;
- }
-
- *a -= adj;
- (*b)++;
- return 1;
+ *b += *a / adj;
+ *a -= adj * (*a / adj);
}
return 0;
}
@@ -90,6 +82,12 @@ static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d)
timelib_sll days_this_month;
timelib_sll last_month, last_year;
timelib_sll days_last_month;
+
+ /* can jump an entire leap year period quickly */
+ if (*d >= DAYS_PER_LYEAR_PERIOD || *d <= -DAYS_PER_LYEAR_PERIOD) {
+ *y += YEARS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
+ *d -= DAYS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
+ }
do_range_limit(1, 13, 12, m, y);
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index 09f43c1b67..4f2af1fb87 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -1032,7 +1032,7 @@ static char *date_format(char *format, int format_len, timelib_time *t, int loca
/* year */
case 'L': length = slprintf(buffer, 32, "%d", timelib_is_leap((int) t->y)); break;
case 'y': length = slprintf(buffer, 32, "%02d", (int) t->y % 100); break;
- case 'Y': length = slprintf(buffer, 32, "%s%04d", t->y < 0 ? "-" : "", abs((int) t->y)); break;
+ case 'Y': length = slprintf(buffer, 32, "%s%04ld", t->y < 0 ? "-" : "", llabs((timelib_sll) t->y)); break;
/* time */
case 'a': length = slprintf(buffer, 32, "%s", t->h >= 12 ? "pm" : "am"); break;
diff --git a/ext/date/tests/bug48058.phpt b/ext/date/tests/bug48058.phpt
new file mode 100644
index 0000000000..290d3df0fc
--- /dev/null
+++ b/ext/date/tests/bug48058.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Bug #48058 (Year formatter goes wrong with out-of-int range)
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+date_default_timezone_set("Europe/London");
+$tz = new DateTimeZone("Europe/London");
+$tran = $tz->getTransitions();
+var_dump( $tran[0] );
+
+$base_time = '28 Feb 2008 12:00:00';
+$dt = date_create( "$base_time +10000000000 years" );
+echo date_format( $dt, DATE_ISO8601 );
+?>
+--EXPECT--
+array(5) {
+ ["ts"]=>
+ int(-9223372036854775808)
+ ["time"]=>
+ string(33) "-292277022657-01-27T08:29:52+0000"
+ ["offset"]=>
+ int(3600)
+ ["isdst"]=>
+ bool(true)
+ ["abbr"]=>
+ string(3) "BST"
+}
+10000002008-02-28T12:00:00+0000
diff --git a/ext/date/tests/date_create-relative.phpt b/ext/date/tests/date_create-relative.phpt
new file mode 100644
index 0000000000..8a90931586
--- /dev/null
+++ b/ext/date/tests/date_create-relative.phpt
@@ -0,0 +1,93 @@
+--TEST--
+date_create() with large relative offsets
+--FILE--
+<?php
+
+date_default_timezone_set('UTC');
+
+if (!defined('PHP_INT_MIN')) {
+ define('PHP_INT_MIN', intval(-PHP_INT_MAX - 1));
+}
+
+$base_time = '28 Feb 2008 12:00:00';
+
+// Most offsets tested in strtotime-relative.phpt. These are tests for dates outside the 32-bit range.
+$offsets = array(
+ // around 10 leap year periods (4000 years) in days
+ '1460000 days',
+ '1460969 days',
+ '1460970 days',
+ '1460971 days',
+ '1462970 days',
+
+ // around 1 leap year period in years
+ '398 years',
+ '399 years',
+ '400 years',
+ '401 years',
+
+ // around 40000 years
+ '39755 years',
+ '39999 years',
+ '40000 years',
+ '40001 years',
+ '41010 years',
+
+ // bigger than int (32-bit)
+ '10000000000 seconds',
+ '10000000000 minutes',
+ '10000000000 hours',
+ '10000000000 days',
+ '10000000000 months',
+ '10000000000 years',
+);
+
+foreach ($offsets AS $offset) {
+ foreach (array('+', '-') AS $direction) {
+ $dt = date_create("$base_time $direction$offset");
+ echo "$direction$offset: " . date_format($dt, DATE_ISO8601) . "\n";
+ }
+}
+
+?>
+--EXPECT--
++1460000 days: 6005-07-03T12:00:00+0000
+-1460000 days: -1990-10-25T12:00:00+0000
++1460969 days: 6008-02-27T12:00:00+0000
+-1460969 days: -1992-02-29T12:00:00+0000
++1460970 days: 6008-02-28T12:00:00+0000
+-1460970 days: -1992-02-28T12:00:00+0000
++1460971 days: 6008-02-29T12:00:00+0000
+-1460971 days: -1992-02-27T12:00:00+0000
++1462970 days: 6013-08-20T12:00:00+0000
+-1462970 days: -1998-09-07T12:00:00+0000
++398 years: 2406-02-28T12:00:00+0000
+-398 years: 1610-02-28T12:00:00+0000
++399 years: 2407-02-28T12:00:00+0000
+-399 years: 1609-02-28T12:00:00+0000
++400 years: 2408-02-28T12:00:00+0000
+-400 years: 1608-02-28T12:00:00+0000
++401 years: 2409-02-28T12:00:00+0000
+-401 years: 1607-02-28T12:00:00+0000
++39755 years: 41763-02-28T12:00:00+0000
+-39755 years: -37747-02-28T12:00:00+0000
++39999 years: 42007-02-28T12:00:00+0000
+-39999 years: -37991-02-28T12:00:00+0000
++40000 years: 42008-02-28T12:00:00+0000
+-40000 years: -37992-02-28T12:00:00+0000
++40001 years: 42009-02-28T12:00:00+0000
+-40001 years: -37993-02-28T12:00:00+0000
++41010 years: 43018-02-28T12:00:00+0000
+-41010 years: -39002-02-28T12:00:00+0000
++10000000000 seconds: 2325-01-18T05:46:40+0000
+-10000000000 seconds: 1691-04-09T18:13:20+0000
++10000000000 minutes: 21021-05-27T22:40:00+0000
+-10000000000 minutes: -17006-12-01T01:20:00+0000
++10000000000 hours: 1142802-09-30T04:00:00+0000
+-10000000000 hours: -1138787-07-28T20:00:00+0000
++10000000000 days: 27381078-03-25T12:00:00+0000
+-10000000000 days: -27377062-02-02T12:00:00+0000
++10000000000 months: 833335341-06-28T12:00:00+0000
+-10000000000 months: -833331326-10-28T12:00:00+0000
++10000000000 years: 10000002008-02-28T12:00:00+0000
+-10000000000 years: -9999997992-02-28T12:00:00+0000
diff --git a/ext/date/tests/strtotime-relative.phpt b/ext/date/tests/strtotime-relative.phpt
new file mode 100644
index 0000000000..769cd2ec2e
--- /dev/null
+++ b/ext/date/tests/strtotime-relative.phpt
@@ -0,0 +1,98 @@
+--TEST--
+strtotime() with relative offsets
+--FILE--
+<?php
+
+date_default_timezone_set('UTC');
+
+$base_time = 1204200000; // 28 Feb 2008 12:00:00
+
+$offsets = array(
+ // offset around a day
+ '80412 seconds',
+ '86399 seconds',
+ '86400 seconds',
+ '86401 seconds',
+ '112913 seconds',
+
+ // offset around 7 days
+ '134 hours',
+ '167 hours',
+ '168 hours',
+ '169 hours',
+ '183 hours',
+
+ // offset around 6 months
+ '178 days',
+ '179 days',
+ '180 days',
+ '183 days',
+ '184 days',
+
+ // offset around 10 years
+ '115 months',
+ '119 months',
+ '120 months',
+ '121 months',
+ '128 months',
+
+ // offset around 25 years (can't do much more reliably with strtotime)
+ '24 years',
+ '25 years',
+ '26 years'
+);
+
+foreach ($offsets AS $offset) {
+ foreach (array('+', '-') AS $direction) {
+ echo "$direction$offset: " . date(DATE_ISO8601, strtotime("$direction$offset", $base_time)) . "\n";
+ }
+}
+
+?>
+--EXPECT--
++80412 seconds: 2008-02-29T10:20:12+0000
+-80412 seconds: 2008-02-27T13:39:48+0000
++86399 seconds: 2008-02-29T11:59:59+0000
+-86399 seconds: 2008-02-27T12:00:01+0000
++86400 seconds: 2008-02-29T12:00:00+0000
+-86400 seconds: 2008-02-27T12:00:00+0000
++86401 seconds: 2008-02-29T12:00:01+0000
+-86401 seconds: 2008-02-27T11:59:59+0000
++112913 seconds: 2008-02-29T19:21:53+0000
+-112913 seconds: 2008-02-27T04:38:07+0000
++134 hours: 2008-03-05T02:00:00+0000
+-134 hours: 2008-02-22T22:00:00+0000
++167 hours: 2008-03-06T11:00:00+0000
+-167 hours: 2008-02-21T13:00:00+0000
++168 hours: 2008-03-06T12:00:00+0000
+-168 hours: 2008-02-21T12:00:00+0000
++169 hours: 2008-03-06T13:00:00+0000
+-169 hours: 2008-02-21T11:00:00+0000
++183 hours: 2008-03-07T03:00:00+0000
+-183 hours: 2008-02-20T21:00:00+0000
++178 days: 2008-08-24T12:00:00+0000
+-178 days: 2007-09-03T12:00:00+0000
++179 days: 2008-08-25T12:00:00+0000
+-179 days: 2007-09-02T12:00:00+0000
++180 days: 2008-08-26T12:00:00+0000
+-180 days: 2007-09-01T12:00:00+0000
++183 days: 2008-08-29T12:00:00+0000
+-183 days: 2007-08-29T12:00:00+0000
++184 days: 2008-08-30T12:00:00+0000
+-184 days: 2007-08-28T12:00:00+0000
++115 months: 2017-09-28T12:00:00+0000
+-115 months: 1998-07-28T12:00:00+0000
++119 months: 2018-01-28T12:00:00+0000
+-119 months: 1998-03-28T12:00:00+0000
++120 months: 2018-02-28T12:00:00+0000
+-120 months: 1998-02-28T12:00:00+0000
++121 months: 2018-03-28T12:00:00+0000
+-121 months: 1998-01-28T12:00:00+0000
++128 months: 2018-10-28T12:00:00+0000
+-128 months: 1997-06-28T12:00:00+0000
++24 years: 2032-02-28T12:00:00+0000
+-24 years: 1984-02-28T12:00:00+0000
++25 years: 2033-02-28T12:00:00+0000
+-25 years: 1983-02-28T12:00:00+0000
++26 years: 2034-02-28T12:00:00+0000
+-26 years: 1982-02-28T12:00:00+0000