diff --git a/core/Date.php b/core/Date.php index f5fd96a4fdd..207b520cbe7 100644 --- a/core/Date.php +++ b/core/Date.php @@ -141,6 +141,11 @@ public static function factory($dateString, $timezone = null) $date = self::lastMonth(); } elseif (is_string($dateString) && preg_match('/last[ -]?year/i', urldecode($dateString))) { $date = self::lastYear(); + } else if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', urldecode($dateString))) { + if (!self::isDateStringValid($dateString)) { + throw self::getFictiousDateException($dateString); + } + $date = new Date(strtotime($dateString)); } elseif ( !is_int($dateString) && ( @@ -175,6 +180,23 @@ public static function factory($dateString, $timezone = null) return Date::factory($timestamp); } + /** + * Returns if the date string is valid + * + * @param $dateString + * @return Boolean + * @ignore + */ + public static function isDateStringValid($dateString) + { + $ret = date_parse($dateString); + if ($ret['warning_count'] || $ret['error_count']) { + return false; + } + return true; + } + + /** * Returns Date w/ UTC timestamp of time $dateString/$timezone. * (Only applies to special strings, like 'now','today','yesterday','yesterdaySameTime'. @@ -1139,6 +1161,13 @@ public static function secondsToDays($secs) return $secs / self::NUM_SECONDS_IN_DAY; } + private static function getFictiousDateException($dateString) + { + $message = Piwik::translate('General_ExceptionFictiousDate'); + return new Exception($message . ": $dateString"); + } + + private static function getInvalidDateFormatException($dateString) { $message = Piwik::translate('General_ExceptionInvalidDateFormat', array("YYYY-MM-DD, or 'today' or 'yesterday'", "strtotime", "http://php.net/strtotime")); diff --git a/lang/en.json b/lang/en.json index 75108f05783..0c0401ee034 100644 --- a/lang/en.json +++ b/lang/en.json @@ -199,6 +199,7 @@ "ExceptionIncompatibleClientServerVersions": "Your %1$s client version is %2$s which is incompatible with server version %3$s.", "ExceptionInvalidAggregateReportsFormat": "Aggregate reports format '%1$s' not valid. Try any of the following instead: %2$s.", "ExceptionInvalidArchiveTimeToLive": "Today archive time to live must be a number of seconds greater than zero", + "ExceptionFictiousDate": "This date does not exist.", "ExceptionInvalidDateBeforeFirstWebsite": "The date '%1$s' is a date before first website was online. Try date that's after %2$s (timestamp %3$s).", "ExceptionInvalidDateFormat": "Date format must be: %1$s or any keyword supported by the %2$s function (see %3$s for more information)", "ExceptionInvalidDateRange": "The date '%1$s' is not a correct date range. It should have the following format: %2$s.", diff --git a/tests/PHPUnit/Unit/DateTest.php b/tests/PHPUnit/Unit/DateTest.php index 76159efb765..ebe18023883 100644 --- a/tests/PHPUnit/Unit/DateTest.php +++ b/tests/PHPUnit/Unit/DateTest.php @@ -415,6 +415,14 @@ public function testIsLeapYear() } } + public function testIsDateStringValid() + { + $this->assertTrue(Date::isDateStringValid('2024-02-29')); + $this->assertFalse(Date::isDateStringValid('2025-02-29')); + $this->assertFalse(Date::isDateStringValid('2025-09-31')); + $this->assertTrue(Date::isDateStringValid('2025-03-31')); + } + public function getLocalizedLongStrings() {