dev/core#2325 import second-handling fix
authorEileen McNaughton <emcnaughton@wikimedia.org>
Fri, 10 Jun 2022 06:50:53 +0000 (18:50 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Fri, 10 Jun 2022 23:12:42 +0000 (11:12 +1200)
CRM/Import/Parser.php
CRM/Utils/Date.php
tests/phpunit/CRM/Utils/DateTest.php

index 7ec35b7c9aa7095af8dc53486a7f44d0d8af5da8..1e84cb17487e35dcfa4c2d904c360558ab73eca2 100644 (file)
@@ -1353,8 +1353,8 @@ abstract class CRM_Import_Parser {
       return 'invalid_import_value';
     }
     if ($fieldMetadata['type'] === CRM_Utils_Type::T_DATE || $fieldMetadata['type'] === (CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME) || $fieldMetadata['type'] === CRM_Utils_Type::T_TIMESTAMP) {
-      $value = CRM_Utils_Date::formatDate($importedValue, $this->getSubmittedValue('dateFormats'));
-      return ($value) ?: 'invalid_import_value';
+      $value = CRM_Utils_Date::formatDate($importedValue, (int) $this->getSubmittedValue('dateFormats'));
+      return $value ?: 'invalid_import_value';
     }
     $options = $this->getFieldOptions($fieldName);
     if ($options !== FALSE) {
index 98e723f60f10725e27497c1ea1732920bc871cc6..1bdabb2b60114330378b0ab278a3eb2719e876f4 100644 (file)
@@ -369,6 +369,7 @@ class CRM_Utils_Date {
 
         $hour24 = (int) substr($dateString, 11, 2);
         $minute = (int) substr($dateString, 14, 2);
+        $second = (int) substr($dateString, 16, 2);
       }
       else {
         $year = (int) substr($dateString, 0, 4);
@@ -377,6 +378,7 @@ class CRM_Utils_Date {
 
         $hour24 = (int) substr($dateString, 8, 2);
         $minute = (int) substr($dateString, 10, 2);
+        $second = (int) substr($dateString, 12, 2);
       }
 
       if ($day % 10 == 1 and $day != 11) {
@@ -430,13 +432,12 @@ class CRM_Utils_Date {
         '%P' => $type,
         '%A' => $type,
         '%Y' => $year,
+        '%s' => str_pad($second, 2, 0, STR_PAD_LEFT),
       ];
 
       return strtr($format, $date);
     }
-    else {
-      return '';
-    }
+    return '';
   }
 
   /**
@@ -2101,15 +2102,23 @@ class CRM_Utils_Date {
   }
 
   /**
+   * Date formatting for imports where date format is specified.
+   *
+   * Note this is used for imports (only) because the importer can
+   * specify the format.
+   *
+   * Tests are in CRM_Utils_DateTest::testFormatDate
+   *
    * @param $date
+   *   Date string as entered.
    * @param $dateType
+   *   One of the constants like CRM_Core_Form_Date::DATE_yyyy_mm_dd.
    *
    * @return null|string
    */
   public static function formatDate($date, $dateType) {
-    $formattedDate = NULL;
     if (empty($date)) {
-      return $formattedDate;
+      return NULL;
     }
 
     // 1. first convert date to default format.
@@ -2122,32 +2131,28 @@ class CRM_Utils_Date {
 
     if (CRM_Utils_Date::convertToDefaultDate($dateParams, $dateType, $dateKey)) {
       $dateVal = $dateParams[$dateKey];
-      $ruleName = 'date';
       if ($dateType == 1) {
         $matches = [];
-        if (preg_match("/(\s(([01]\d)|[2][0-3]):([0-5]\d):?[0-5]?\d?)$/", $date, $matches)) {
-          $ruleName = 'dateTime';
+        // The seconds part of this regex is not quite right - but it does succeed
+        // in clarifying whether there is a time component or not - which is all it is meant
+        // to do.
+        if (preg_match('/(\s(([01]\d)|[2][0-3]):([0-5]\d):?[0-5]?\d?)$/', $date, $matches)) {
           if (strpos($date, '-') !== FALSE) {
             $dateVal .= array_shift($matches);
           }
+          if (!CRM_Utils_Rule::dateTime($dateVal)) {
+            return NULL;
+          }
+          $dateVal = CRM_Utils_Date::customFormat(preg_replace("/(:|\s)?/", '', $dateVal), '%Y%m%d%H%i%s');
+          return $dateVal;
         }
       }
 
       // validate date.
-      $valid = CRM_Utils_Rule::$ruleName($dateVal);
-
-      if ($valid) {
-        // format date and time to default.
-        if ($ruleName == 'dateTime') {
-          $dateVal = CRM_Utils_Date::customFormat(preg_replace("/(:|\s)?/", "", $dateVal), '%Y%m%d%H%i');
-          // hack to add seconds
-          $dateVal .= '00';
-        }
-        $formattedDate = $dateVal;
-      }
+      return CRM_Utils_Rule::date($dateVal) ? $dateVal : NULL;
     }
 
-    return $formattedDate;
+    return NULL;
   }
 
   /**
index b0917741ef2837e9df9f81fbc30034b5047d4cdd..8ab957fbbe41ee5f4941190391ed007aa17b4ffb 100644 (file)
@@ -68,7 +68,7 @@ class CRM_Utils_DateTest extends CiviUnitTestCase {
     $cases = $this->fromToData();
     foreach ($cases as $caseDescription => $case) {
       $obj = new CRM_Utils_Date();
-      list($calculatedFrom, $calculatedTo) = $obj->getFromTo($case['relative'], $case['from'], $case['to']);
+      [$calculatedFrom, $calculatedTo] = $obj->getFromTo($case['relative'], $case['from'], $case['to']);
       $this->assertEquals($case['expectedFrom'], $calculatedFrom, "Expected From failed for case $caseDescription");
       $this->assertEquals($case['expectedTo'], $calculatedTo, "Expected To failed for case $caseDescription");
     }
@@ -324,4 +324,29 @@ class CRM_Utils_DateTest extends CiviUnitTestCase {
     }
   }
 
+  /**
+   * Test formatDate function.
+   *
+   * @dataProvider dateDataProvider
+   *
+   * Test the format function used in imports. Note most forms
+   * are able to format pre-submit but the import needs to parse the date.
+   */
+  public function testFormatDate($date, $format, $expected): void {
+    $this->assertEquals($expected, CRM_Utils_Date::formatDate($date, $format));
+  }
+
+  /**
+   * Data provider for date formats.
+   *
+   * @return array[]
+   */
+  public function dateDataProvider(): array {
+    return [
+      ['date' => '2022-10-01', 'format' => CRM_Core_Form_Date::DATE_yyyy_mm_dd, 'expected' => '20221001'],
+      ['date' => '2022-10-01 15:54', 'format' => CRM_Core_Form_Date::DATE_yyyy_mm_dd, 'expected' => '20221001155400'],
+      ['date' => '2022-10-01 15:54:56', 'format' => CRM_Core_Form_Date::DATE_yyyy_mm_dd, 'expected' => '20221001155456'],
+    ];
+  }
+
 }