if (empty($date) || !self::validateDateInput($date, $dateType)) {
return NULL;
}
- if ($dateType === self::DATE_yyyy_mm_dd) {
+ if ($dateType === self::DATE_mm_dd_yy || $dateType === self::DATE_mm_dd_yyyy) {
+ // PHP interprets slashes as American and dots/dashes as European/other.
+ // The only thing we support for mm_dd_yy that differs from strtotime is
+ // the use of dashes - so here we replace then we can use strtotime.
+ $date = str_replace('-', '/', $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], 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_mm_dd_yy || $dateType === self::DATE_mm_dd_yyyy) {
- $formattedDate = explode("/", $value);
- if (count($formattedDate) != 3) {
- $formattedDate = explode("-", $value);
- }
- if (count($formattedDate) == 3) {
- $year = (int) $formattedDate[2];
- $month = (int) $formattedDate[0];
- $day = (int) $formattedDate[1];
- }
- else {
- return NULL;
- }
- }
if ($dateType === self::DATE_Month_dd_yyyy) {
$dateArray = explode(' ', $value);
// ignore comma(,)
$month = ($month < 10) ? "0" . "$month" : $month;
$day = ($day < 10) ? "0" . "$day" : $day;
- $year = (int) $year;
- if ($year < 100) {
- $year = substr($now['year'], 0, 2) * 100 + $year;
- if ($year > ($now['year'] + 5)) {
- $year = $year - 100;
- }
- elseif ($year <= ($now['year'] - 95)) {
- $year = $year + 100;
- }
- }
+ $year = self::getYear($year, $now['year']);
$newDate = "$year$month$day";
// if month is invalid return as error
return TRUE;
}
+ /**
+ * Get the date element from the passed date string.
+ *
+ * @param string $date e.g. '20-Oct-2022'
+ * @param string $separator e.g '-'
+ * @param int $monthPlacement eg. 2 for the second section of the string
+ *
+ * @return string
+ */
+ protected static function getDateElement(string $date, string $separator, int $monthPlacement): string {
+ $element = explode($separator, $date)[$monthPlacement - 1];
+ // This second explosion drops any trailing time string.
+ return explode(' ', $element)[0];
+ }
+
+ /**
+ * Replace a year in the short year format e.g 22.
+ *
+ * Note this differs from standard php strotime as we treat anything less
+ * than 5 years in the future as being in the past.
+ *
+ * The reasons for this are not documented but it is likely that our use cases
+ * dictated it - eg. importing birth dates would more sanely default to handling 68
+ * as 1968. By contrast importing future data is likely rare.
+ *
+ * @param string $date
+ * @param string $separator
+ * @param int $yearPlacement
+ *
+ * @return string
+ */
+ public static function replaceShortYear($date, string $separator, int $yearPlacement): string {
+ $year = self::getDateElement($date, $separator, $yearPlacement);
+ if (strlen($year) === 4) {
+ return $date;
+ }
+ $parts = explode($separator, $date);
+ // Replace the year with the 4-digit-year, re-appending any trailing time string.
+ $parts[$yearPlacement - 1] = self::getYear($year) . substr($parts[$yearPlacement - 1], 2);
+ return implode($separator, $parts);
+ }
+
+ /**
+ * Get a 4 digit year from a 2 or 4 digit year.
+ *
+ * The handling differs from strtotime as a year more than 5 years in the future
+ * is deemed to be in the past whereas strtotime uses a 1970 cutoff
+ * https://www.w3schools.com/php/func_date_strtotime.asp
+ *
+ * @param int $year
+ *
+ * @return int
+ */
+ public static function getYear(int $year) {
+ $currentYear = date('Y');
+ if ($year < 100) {
+ $year = ((int) substr($currentYear, 0, 2)) * 100 + $year;
+ if ($year > ($currentYear + 5)) {
+ $year -= 100;
+ }
+ elseif ($year <= ($currentYear - 95)) {
+ $year += 100;
+ }
+ }
+ return $year;
+ }
+
}
'2022-10-01 3:54:56' => ['date' => '2022-10-01 3:54:56', 'format' => CRM_Utils_Date::DATE_yyyy_mm_dd, 'expected' => '20221001035456'],
// mm_dd_yy format - eg. US Style 10-01-22 OR 10/01/22 where 10 is the month. 2 digit year.
- '10-01-22' => ['date' => '10-01-22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001'],
- '10-1-22' => ['date' => '10-1-22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001'],
- '10-01-22 15:54:56' => ['date' => '10-01-22 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001155456', 'ignore_reason' => 'Time not handled correctly in this instance.'],
- '10-1-22 3:54:56' => ['date' => '10-1-22 3:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001035456', 'ignore_reason' => 'Time not handled correctly in this instance.'],
- '10/01/22' => ['date' => '10/01/22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001'],
- '10/1/22' => ['date' => '10/1/22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001'],
- '10/01/22 15:54:56' => ['date' => '10/01/22 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001155456', 'ignore_reason' => 'Time not handled correctly in this instance.'],
- '10/01/22 3:54:56' => ['date' => '10/01/22 3:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001035456', 'ignore_reason' => 'Time not handled correctly in this instance.'],
+ '10-01-30-mapped-to-1934-not-2034-per-strtotime' => ['date' => '10-01-34', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '19341001000000'],
+ '10-01-22' => ['date' => '10-01-22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001000000'],
+ '10-1-22' => ['date' => '10-1-22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001000000'],
+ '10-01-22 15:54:56' => ['date' => '10-01-22 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001155456'],
+ '10-1-22 3:54:56' => ['date' => '10-1-22 3:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001035456'],
+ '10/01/22' => ['date' => '10/01/22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001000000'],
+ '10/1/22' => ['date' => '10/1/22', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001000000'],
+ '10/01/22 15:54:56' => ['date' => '10/01/22 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001155456'],
+ '10/01/22 3:54:56' => ['date' => '10/01/22 3:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yy, 'expected' => '20221001035456'],
// mm_dd_yyyy format - eg. US Style 10-01-2022 OR 10/01/2022 where 10 is the month. 4 digit year.
- '10-01-2022' => ['date' => '10-01-2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001'],
- '10-1-2022' => ['date' => '10-1-2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001'],
- '10-01-2022 15:54:56' => ['date' => '10-01-2022 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001155456', 'ignore_reason' => 'Time not handled correctly in this instance.'],
- '10-01-2022 3:54:56' => ['date' => '10-01-2022 3:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001035456', 'ignore_reason' => 'Time not handled correctly in this instance.'],
- '10/01/2022' => ['date' => '10/01/2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001'],
- '10/1/2022' => ['date' => '10/1/2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001'],
- '10/1/2022 15:54:56' => ['date' => '10/1/2022 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001155456', 'ignore_reason' => 'Time not handled correctly in this instance.'],
+ '10-01-2022' => ['date' => '10-01-2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001000000'],
+ '10-1-2022' => ['date' => '10-1-2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001000000'],
+ '10-01-2022 15:54:56' => ['date' => '10-01-2022 15:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001155456'],
+ '10-01-2022 3:54:56' => ['date' => '10-01-2022 3:54:56', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001035456'],
+ '10/01/2022' => ['date' => '10/01/2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001000000'],
+ '10/1/2022' => ['date' => '10/1/2022', 'format' => CRM_Utils_Date::DATE_mm_dd_yyyy, 'expected' => '20221001000000'],
+ '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'],