diff options
author | Derick Rethans <derick@php.net> | 2009-05-03 18:22:40 +0000 |
---|---|---|
committer | Derick Rethans <derick@php.net> | 2009-05-03 18:22:40 +0000 |
commit | febc2763dd872d484ed7df09f778a0a85104102a (patch) | |
tree | 8a8b86fea5dd273a9b8901f96e2a17d09c5d8059 | |
parent | 7e0387dbde7f0d3da6c948b29f036dedb4bbc248 (diff) | |
download | php-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.h | 3 | ||||
-rw-r--r-- | ext/date/lib/tm2unixtime.c | 22 | ||||
-rw-r--r-- | ext/date/php_date.c | 2 | ||||
-rw-r--r-- | ext/date/tests/bug48058.phpt | 29 | ||||
-rw-r--r-- | ext/date/tests/date_create-relative.phpt | 93 | ||||
-rw-r--r-- | ext/date/tests/strtotime-relative.phpt | 98 |
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 |