From 9b307e296f19ad457da43674a178de6c530047cb Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 3 Jan 2024 15:01:35 +1300 Subject: [PATCH] Fix time-handling for DATE_Month_dd_yyyy format --- CRM/Utils/Date.php | 62 +++++++++++++++------------- tests/phpunit/CRM/Utils/DateTest.php | 9 ++-- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/CRM/Utils/Date.php b/CRM/Utils/Date.php index 0775a22368..de3943818f 100644 --- a/CRM/Utils/Date.php +++ b/CRM/Utils/Date.php @@ -767,10 +767,12 @@ class CRM_Utils_Date { return preg_match('/^(\d|\d\d)[-\/](\d|\d\d)[-\/]\d\d\d\d$/', $inputValue); case self::DATE_Month_dd_yyyy: - return preg_match('/^[A-Za-z]*.[ \t]?\d\d\,[ \t]?\d\d\d\d$/', $inputValue); + $monthRegex = self::getMonthRegex(); + $regex = '/^' . $monthRegex . ',?\s?(\d|\d\d),?\s]?(\d\d|\d\d\d\d)$/i'; + return preg_match($regex, $inputValue); case self::DATE_dd_mon_yy: - return preg_match('/^(\d|\d\d)-[A-Za-z]{3}.*-\d\d$/', $inputValue) || preg_match('/^(\d|\d\d)[-\/](\d|\d\d)[-\/]\d\d$/', $inputValue); + return preg_match('/^(\d|\d\d)-' . self::getMonthRegex() . '-\d\d$/i', $inputValue) || preg_match('/^(\d|\d\d)[-\/](\d|\d\d)[-\/]\d\d$/', $inputValue); case self::DATE_dd_mm_yyyy: return preg_match('/^(\d|\d\d)[-\/](\d|\d\d)[-\/]\d\d\d\d/', $inputValue); @@ -2224,35 +2226,21 @@ class CRM_Utils_Date { $date = self::replaceShortYear($date, '-', 3); } - if (in_array($dateType, [self::DATE_yyyy_mm_dd, self::DATE_mm_dd_yy, self::DATE_mm_dd_yyyy, self::DATE_dd_mm_yyyy, self::DATE_dd_mon_yy], TRUE)) { - $timestamp = strtotime($date); - return $timestamp ? date('YmdHis', $timestamp) : NULL; - } - - $now = getdate(); - // suppress hh:mm or hh:mm:ss if it exists CRM-7957 - $value = preg_replace(self::getTimeRegex(), "", $date); if ($dateType === self::DATE_Month_dd_yyyy) { - $dateArray = explode(' ', $value); - // ignore comma(,) - $dateArray[1] = (int) substr($dateArray[1], 0, 2); - - $month = self::getNumericMonth($dateArray[0]); - $year = (int) $dateArray[2]; - $day = (int) $dateArray[1]; + $timeMatches = []; + preg_match(self::getTimeRegex(), $date, $timeMatches); + $timePortion = $timeMatches[0] ?? ''; + $datePortion = str_replace($timePortion, '', $date); + // Replace whitespace in the date portion of the string with '/' for strtotime to handle + // as US-ordered values. We strip off & re-add the time portion + // so that part is not changed (ie a space continues to separate the date & time). + $date = preg_replace('/(,|\s)+/', '/', $datePortion) . $timePortion; + $date = self::replaceTextMonth($date, '/', 1); + $date = self::replaceShortYear($date, '/', 3); } - $day = ($day < 10) ? "0" . "$day" : $day; - - $year = self::getYear($year, $now['year']); - - $newDate = "$year$month$day"; - // if month is invalid return as error - if ($month !== '00' && $month <= 12) { - // validate date. - return CRM_Utils_Rule::date($newDate) ? $newDate : NULL; - } - return NULL; + $timestamp = strtotime($date); + return $timestamp ? date('YmdHis', $timestamp) : NULL; } /** @@ -2388,7 +2376,7 @@ class CRM_Utils_Date { if (is_numeric($string)) { return $string; } - $string = strtolower(trim($string, '.')); + $string = strtolower(trim($string, "., \n\r\t\v\0")); foreach (self::getFullMonthNames() as $monthNumeric => $monthName) { if ($string === mb_strtolower($monthName)) { return str_pad($monthNumeric, 2, 0, STR_PAD_LEFT); @@ -2492,4 +2480,20 @@ class CRM_Utils_Date { return $year; } + /** + * Get the regex to find a locale-relevant date in the string. + * + * Resulting regex looks like this, in English locale + * /^(January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec),?\s?\d\d,?\s]?\d\d\d\d$/ + * + * + * @return string + * @internal + * + */ + protected static function getMonthRegex(): string { + $months = array_merge(self::getFullMonthNames(), self::getAbbrMonthNames()); + return '(' . implode('|', $months) . ')'; + } + } diff --git a/tests/phpunit/CRM/Utils/DateTest.php b/tests/phpunit/CRM/Utils/DateTest.php index 039cbe0139..381f6de16c 100644 --- a/tests/phpunit/CRM/Utils/DateTest.php +++ b/tests/phpunit/CRM/Utils/DateTest.php @@ -2712,7 +2712,12 @@ class CRM_Utils_DateTest extends CiviUnitTestCase { '10/1/2022 15:54:56' => ['date' => '10/1/2022 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001155456'], // DATE_Month_dd_yyyy ie December, 12 2023 - 'December, 12 2023' => ['date' => 'December, 12 2023', 'format' => CRM_Utils_Date::DATE_Month_dd_yyyy, 'expected' => '20221001035456', 'ignore_reason' => 'Example syntax is broken. Investigate'], + 'December, 11 2023' => ['date' => 'December, 11 2023', 'format' => CRM_Utils_Date::DATE_Month_dd_yyyy, 'expected' => '20231211000000'], + 'December, 1 2023' => ['date' => 'December, 1 2023', 'format' => CRM_Utils_Date::DATE_Month_dd_yyyy, 'expected' => '20231201000000'], + 'December, 1 23' => ['date' => 'December, 1 23', 'format' => CRM_Utils_Date::DATE_Month_dd_yyyy, 'expected' => '20231201000000'], + 'December, 1 2023-with-tab' => ['date' => "December,\t1 2023", 'format' => CRM_Utils_Date::DATE_Month_dd_yyyy, 'expected' => '20231201000000'], + 'December 1 2023-no-comma' => ['date' => 'December 1 2023', 'format' => CRM_Utils_Date::DATE_Month_dd_yyyy, 'expected' => '20231201000000'], + 'December 1 2023 15:35:35' => ['date' => 'December 1 2023 15:35:35', 'format' => CRM_Utils_Date::DATE_Month_dd_yyyy, 'expected' => '20231201153535'], // dd_mon_yy format (NZ, Australia) - eg. 01-10-22 or 01/10/22 WHERE 01 is the DAY. 2 digit year. '01/10/22' => ['date' => '01/10/22', 'format' => CRM_Utils_Date::DATE_dd_mon_yy, 'expected' => '20221001000000'], @@ -2728,8 +2733,6 @@ class CRM_Utils_DateTest extends CiviUnitTestCase { '1/10/2022' => ['date' => '1/10/2022', 'format' => CRM_Utils_Date::DATE_dd_mm_yyyy, 'expected' => '20221001000000'], '1/10/2022 15:54:56' => ['date' => '1/10/2022 15:54:56', 'format' => CRM_Utils_Date::DATE_dd_mm_yyyy, 'expected' => '20221001155456'], '1/10/2022 3:54:56' => ['date' => '1/10/2022 3:54:56', 'format' => CRM_Utils_Date::DATE_dd_mm_yyyy, 'expected' => '20221001035456'], - '1-Oct-2022 15:54:56' => ['date' => '1-Oct-2022 15:54:56', 'format' => CRM_Utils_Date::DATE_dd_mm_yyyy, 'expected' => '20221001155456', 'ignore_reason' => 'Time not handled correctly in this instance.'], - '1-Oct-2022 3:54:56' => ['date' => '1-Oct-2022 3:54:56', 'format' => CRM_Utils_Date::DATE_dd_mm_yyyy, 'expected' => '20221001035456', 'ignore_reason' => 'Time not handled correctly in this instance.'], '1-10-2022 15:54:56' => ['date' => '1-10-2022 15:54:56', 'format' => CRM_Utils_Date::DATE_dd_mm_yyyy, 'expected' => '20221001155456'], '1-10-2022 3:54:56' => ['date' => '1-10-2022 3:54:56', 'format' => CRM_Utils_Date::DATE_dd_mm_yyyy, 'expected' => '20221001035456'], ]; -- 2.25.1