From d22d17ed5e08ba9c61c10e1512f5995a392fb3aa Mon Sep 17 00:00:00 2001 From: diosmosis Date: Sun, 31 Aug 2014 01:18:49 -0700 Subject: Fix bug in Date::addPeriod(N, 'month') where max number of days in a month was not respected. So if subtracting one month from July 31, result would be July 1. Now result is June 30th. Note: there appears to be little to no change in speed, tested both in integration tests and the code by itself. --- core/Date.php | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) (limited to 'core/Date.php') diff --git a/core/Date.php b/core/Date.php index 7af59e1f2a..c5b4cd7af6 100644 --- a/core/Date.php +++ b/core/Date.php @@ -40,6 +40,26 @@ class Date /** The default date time string format. */ const DATE_TIME_FORMAT = 'Y-m-d H:i:s'; + /** + * Max days for months (non-leap-year). See {@link addPeriod()} implementation. + * + * @var int[] + */ + private static $maxDaysInMonth = array( + '1' => 31, + '2' => 28, + '3' => 31, + '4' => 30, + '5' => 31, + '6' => 30, + '7' => 31, + '8' => 31, + '9' => 30, + '10' => 31, + '11' => 30, + '12' => 31 + ); + /** * The stored timestamp is always UTC based. * The returned timestamp via getTimestamp() will have the conversion applied @@ -673,14 +693,41 @@ class Date */ public function addPeriod($n, $period) { - if ($n < 0) { - $ts = strtotime("$n $period", $this->timestamp); + if (strtolower($period) == 'month') { // TODO: comments + $dateInfo = getdate($this->timestamp); + + $ts = mktime( + $dateInfo['hours'], + $dateInfo['minutes'], + $dateInfo['seconds'], + $dateInfo['mon'] + (int)$n, + 1, + $dateInfo['year'] + ); + + $daysToAdd = min($dateInfo['mday'], self::getMaxDaysInMonth($ts)) - 1; + $ts += self::NUM_SECONDS_IN_DAY * $daysToAdd; } else { - $ts = strtotime("+$n $period", $this->timestamp); + $time = $n < 0 ? "$n $period" : "+$n $period"; + + $ts = strtotime($time, $this->timestamp); } + return new Date($ts, $this->timezone); } + private static function getMaxDaysInMonth($timestamp) + { + $month = (int)date('m', $timestamp); + if (date('L', $timestamp) == 1 + && $month == 2 + ) { + return 29; + } else { + return self::$maxDaysInMonth[$month]; + } + } + /** * Subtracts a period from `$this` date and returns the result in a new Date instance. * -- cgit v1.2.3