Move another chunk of code to fx_date_helper.cpp.

In this step, several methods need to become public as they are
used both locally and in cjs_publicmethods.cpp. Same too for the
kMonth/kFullMonth arrays (now namespaced).

Move the non-JS parts of ParseDateUsingFormat as well.

Change-Id: Iff19a3aa94be39bf84d694f2b2e02224e0a36300
Reviewed-on: https://pdfium-review.googlesource.com/c/45032
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/fxjs/cjs_publicmethods.cpp b/fxjs/cjs_publicmethods.cpp
index e26af63..436e802 100644
--- a/fxjs/cjs_publicmethods.cpp
+++ b/fxjs/cjs_publicmethods.cpp
@@ -66,15 +66,6 @@
 constexpr double kDoubleCorrect = 0.000000000000001;
 #endif
 
-constexpr const wchar_t* kMonths[] = {L"Jan", L"Feb", L"Mar", L"Apr",
-                                      L"May", L"Jun", L"Jul", L"Aug",
-                                      L"Sep", L"Oct", L"Nov", L"Dec"};
-
-constexpr const wchar_t* kFullMonths[] = {L"January", L"February", L"March",
-                                          L"April",   L"May",      L"June",
-                                          L"July",    L"August",   L"September",
-                                          L"October", L"November", L"December"};
-
 constexpr const wchar_t* kDateFormats[] = {L"m/d",
                                            L"m/d/yy",
                                            L"mm/dd/yy",
@@ -207,27 +198,6 @@
   str->Replace(L",", L".");
 }
 
-bool IsValidMonth(int m) {
-  return m >= 1 && m <= 12;
-}
-
-// TODO(thestig): Should this take the month into consideration?
-bool IsValidDay(int d) {
-  return d >= 1 && d <= 31;
-}
-
-// TODO(thestig): Should 24 be allowed? Similarly, 60 for minutes and seconds.
-bool IsValid24Hour(int h) {
-  return h >= 0 && h <= 24;
-}
-
-bool IsValidMinute(int m) {
-  return m >= 0 && m <= 60;
-}
-
-bool IsValidSecond(int s) {
-  return s >= 0 && s <= 60;
-}
 
 }  // namespace
 
@@ -406,10 +376,10 @@
     // TODO(thestig): Should the else case set |bWrongFormat| to true?
     // case2: month/day
     // case3: day/month
-    if (IsValidMonth(number[0]) && IsValidDay(number[1])) {
+    if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1])) {
       nMonth = number[0];
       nDay = number[1];
-    } else if (IsValidDay(number[0]) && IsValidMonth(number[1])) {
+    } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1])) {
       nDay = number[0];
       nMonth = number[1];
     }
@@ -421,16 +391,17 @@
     // case1: year/month/day
     // case2: month/day/year
     // case3: day/month/year
-    if (number[0] > 12 && IsValidMonth(number[1]) && IsValidDay(number[2])) {
+    if (number[0] > 12 && FX_IsValidMonth(number[1]) &&
+        FX_IsValidDay(number[2])) {
       nYear = number[0];
       nMonth = number[1];
       nDay = number[2];
-    } else if (IsValidMonth(number[0]) && IsValidDay(number[1]) &&
+    } else if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1]) &&
                number[2] > 31) {
       nMonth = number[0];
       nDay = number[1];
       nYear = number[2];
-    } else if (IsValidDay(number[0]) && IsValidMonth(number[1]) &&
+    } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1]) &&
                number[2] > 31) {
       nDay = number[0];
       nMonth = number[1];
@@ -453,260 +424,19 @@
 double CJS_PublicMethods::ParseDateUsingFormat(const WideString& value,
                                                const WideString& format,
                                                bool* bWrongFormat) {
-  double dt = FX_GetDateTime();
+  double dRet = std::nan("");
+  fxjs::ConversionStatus status = FX_ParseDateUsingFormat(value, format, &dRet);
+  if (status == fxjs::ConversionStatus::kSuccess)
+    return dRet;
 
-  if (format.IsEmpty() || value.IsEmpty())
-    return dt;
+  if (status == fxjs::ConversionStatus::kBadDate) {
+    dRet = JS_DateParse(value);
+    if (!std::isnan(dRet))
+      return dRet;
+  }
 
-  int nYear = FX_GetYearFromTime(dt);
-  int nMonth = FX_GetMonthFromTime(dt) + 1;
-  int nDay = FX_GetDayFromTime(dt);
-  int nHour = FX_GetHourFromTime(dt);
-  int nMin = FX_GetMinFromTime(dt);
-  int nSec = FX_GetSecFromTime(dt);
-
-  int nYearSub = 99;  // nYear - 2000;
-
-  bool bPm = false;
-  bool bExit = false;
   bool bBadFormat = false;
-
-  size_t i = 0;
-  size_t j = 0;
-
-  while (i < format.GetLength()) {
-    if (bExit)
-      break;
-
-    wchar_t c = format[i];
-    switch (c) {
-      case ':':
-      case '.':
-      case '-':
-      case '\\':
-      case '/':
-        i++;
-        j++;
-        break;
-
-      case 'y':
-      case 'm':
-      case 'd':
-      case 'H':
-      case 'h':
-      case 'M':
-      case 's':
-      case 't': {
-        size_t oldj = j;
-        size_t nSkip = 0;
-        size_t remaining = format.GetLength() - i - 1;
-
-        if (remaining == 0 || format[i + 1] != c) {
-          switch (c) {
-            case 'y':
-              i++;
-              j++;
-              break;
-            case 'm':
-              nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'd':
-              nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'H':
-              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'h':
-              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'M':
-              nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 's':
-              nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 't':
-              bPm = (j < value.GetLength() && value[j] == 'p');
-              i++;
-              j++;
-              break;
-          }
-        } else if (remaining == 1 || format[i + 2] != c) {
-          switch (c) {
-            case 'y':
-              nYear = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'm':
-              nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'd':
-              nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'H':
-              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'h':
-              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'M':
-              nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 's':
-              nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 't':
-              bPm = (j + 1 < value.GetLength() && value[j] == 'p' &&
-                     value[j + 1] == 'm');
-              i += 2;
-              j += 2;
-              break;
-          }
-        } else if (remaining == 2 || format[i + 3] != c) {
-          switch (c) {
-            case 'm': {
-              WideString sMonth = FX_ParseStringString(value, j, &nSkip);
-              bool bFind = false;
-              for (int m = 0; m < 12; m++) {
-                if (sMonth.CompareNoCase(kMonths[m]) == 0) {
-                  nMonth = m + 1;
-                  i += 3;
-                  j += nSkip;
-                  bFind = true;
-                  break;
-                }
-              }
-
-              if (!bFind) {
-                nMonth = FX_ParseStringInteger(value, j, &nSkip, 3);
-                i += 3;
-                j += nSkip;
-              }
-            } break;
-            case 'y':
-              break;
-            default:
-              i += 3;
-              j += 3;
-              break;
-          }
-        } else if (remaining == 3 || format[i + 4] != c) {
-          switch (c) {
-            case 'y':
-              nYear = FX_ParseStringInteger(value, j, &nSkip, 4);
-              j += nSkip;
-              i += 4;
-              break;
-            case 'm': {
-              bool bFind = false;
-
-              WideString sMonth = FX_ParseStringString(value, j, &nSkip);
-              sMonth.MakeLower();
-
-              for (int m = 0; m < 12; m++) {
-                WideString sFullMonths = WideString(kFullMonths[m]);
-                sFullMonths.MakeLower();
-
-                if (sFullMonths.Contains(sMonth.c_str())) {
-                  nMonth = m + 1;
-                  i += 4;
-                  j += nSkip;
-                  bFind = true;
-                  break;
-                }
-              }
-
-              if (!bFind) {
-                nMonth = FX_ParseStringInteger(value, j, &nSkip, 4);
-                i += 4;
-                j += nSkip;
-              }
-            } break;
-            default:
-              i += 4;
-              j += 4;
-              break;
-          }
-        } else {
-          if (j >= value.GetLength() || format[i] != value[j]) {
-            bBadFormat = true;
-            bExit = true;
-          }
-          i++;
-          j++;
-        }
-
-        if (oldj == j) {
-          bBadFormat = true;
-          bExit = true;
-        }
-        break;
-      }
-
-      default:
-        if (value.GetLength() <= j) {
-          bExit = true;
-        } else if (format[i] != value[j]) {
-          bBadFormat = true;
-          bExit = true;
-        }
-
-        i++;
-        j++;
-        break;
-    }
-  }
-
-  if (bPm)
-    nHour += 12;
-
-  if (nYear >= 0 && nYear <= nYearSub)
-    nYear += 2000;
-
-  if (!bBadFormat) {
-    bBadFormat = !IsValidMonth(nMonth) || !IsValidDay(nDay) ||
-                 !IsValid24Hour(nHour) || !IsValidMinute(nMin) ||
-                 !IsValidSecond(nSec);
-  }
-
-  double dRet;
-  if (bBadFormat) {
-    dRet = ParseDate(value, &bBadFormat);
-  } else {
-    dRet = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
-                       FX_MakeTime(nHour, nMin, nSec, 0));
-    if (std::isnan(dRet))
-      dRet = JS_DateParse(value);
-  }
-
-  if (std::isnan(dRet))
-    dRet = ParseDate(value, &bBadFormat);
-
+  dRet = ParseDate(value, &bBadFormat);
   if (bWrongFormat)
     *bWrongFormat = bBadFormat;
 
@@ -801,8 +531,8 @@
           switch (c) {
             case 'm':
               i += 3;
-              if (IsValidMonth(nMonth))
-                sPart += kMonths[nMonth - 1];
+              if (FX_IsValidMonth(nMonth))
+                sPart += fxjs::kMonths[nMonth - 1];
               break;
             default:
               i += 3;
@@ -819,8 +549,8 @@
               break;
             case 'm':
               i += 4;
-              if (IsValidMonth(nMonth))
-                sPart += kFullMonths[nMonth - 1];
+              if (FX_IsValidMonth(nMonth))
+                sPart += fxjs::kFullMonths[nMonth - 1];
               break;
             default:
               i += 4;
@@ -1184,8 +914,8 @@
 
   int nMonth = 1;
   sTemp = wsArray[1];
-  for (size_t i = 0; i < FX_ArraySize(kMonths); ++i) {
-    if (sTemp.Compare(kMonths[i]) == 0) {
+  for (size_t i = 0; i < FX_ArraySize(fxjs::kMonths); ++i) {
+    if (sTemp.Compare(fxjs::kMonths[i]) == 0) {
       nMonth = i + 1;
       break;
     }
@@ -1225,8 +955,8 @@
   if (strValue.IsEmpty())
     return CJS_Result::Success();
 
-  WideString sFormat = pRuntime->ToWideString(params[0]);
   bool bWrongFormat = false;
+  WideString sFormat = pRuntime->ToWideString(params[0]);
   double dRet = ParseDateUsingFormat(strValue, sFormat, &bWrongFormat);
   if (bWrongFormat || std::isnan(dRet)) {
     WideString swMsg = WideString::Format(
diff --git a/fxjs/fx_date_helpers.cpp b/fxjs/fx_date_helpers.cpp
index 403a731..847e8c3 100644
--- a/fxjs/fx_date_helpers.cpp
+++ b/fxjs/fx_date_helpers.cpp
@@ -8,6 +8,8 @@
 
 #include <time.h>
 
+#include <cmath>
+
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_system.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
@@ -164,6 +166,15 @@
 
 }  // namespace
 
+const wchar_t* const kMonths[12] = {L"Jan", L"Feb", L"Mar", L"Apr",
+                                    L"May", L"Jun", L"Jul", L"Aug",
+                                    L"Sep", L"Oct", L"Nov", L"Dec"};
+
+const wchar_t* const kFullMonths[12] = {L"January", L"February", L"March",
+                                        L"April",   L"May",      L"June",
+                                        L"July",    L"August",   L"September",
+                                        L"October", L"November", L"December"};
+
 double FX_GetDateTime() {
   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
     return 0;
@@ -199,6 +210,28 @@
   return (int)Mod(floor(dt / 1000), 60);
 }
 
+bool FX_IsValidMonth(int m) {
+  return m >= 1 && m <= 12;
+}
+
+// TODO(thestig): Should this take the month into consideration?
+bool FX_IsValidDay(int d) {
+  return d >= 1 && d <= 31;
+}
+
+// TODO(thestig): Should 24 be allowed? Similarly, 60 for minutes and seconds.
+bool FX_IsValid24Hour(int h) {
+  return h >= 0 && h <= 24;
+}
+
+bool FX_IsValidMinute(int m) {
+  return m >= 0 && m <= 60;
+}
+
+bool FX_IsValidSecond(int s) {
+  return s >= 0 && s <= 60;
+}
+
 double FX_LocalTime(double d) {
   return d + GetLocalTZA() + GetDaylightSavingTA(d);
 }
@@ -272,4 +305,258 @@
   return swRet;
 }
 
+ConversionStatus FX_ParseDateUsingFormat(const WideString& value,
+                                         const WideString& format,
+                                         double* result) {
+  double dt = FX_GetDateTime();
+  if (format.IsEmpty() || value.IsEmpty()) {
+    *result = dt;
+    return ConversionStatus::kSuccess;
+  }
+
+  int nYear = FX_GetYearFromTime(dt);
+  int nMonth = FX_GetMonthFromTime(dt) + 1;
+  int nDay = FX_GetDayFromTime(dt);
+  int nHour = FX_GetHourFromTime(dt);
+  int nMin = FX_GetMinFromTime(dt);
+  int nSec = FX_GetSecFromTime(dt);
+  int nYearSub = 99;  // nYear - 2000;
+  bool bPm = false;
+  bool bExit = false;
+  bool bBadFormat = false;
+  size_t i = 0;
+  size_t j = 0;
+
+  while (i < format.GetLength()) {
+    if (bExit)
+      break;
+
+    wchar_t c = format[i];
+    switch (c) {
+      case ':':
+      case '.':
+      case '-':
+      case '\\':
+      case '/':
+        i++;
+        j++;
+        break;
+
+      case 'y':
+      case 'm':
+      case 'd':
+      case 'H':
+      case 'h':
+      case 'M':
+      case 's':
+      case 't': {
+        size_t oldj = j;
+        size_t nSkip = 0;
+        size_t remaining = format.GetLength() - i - 1;
+
+        if (remaining == 0 || format[i + 1] != c) {
+          switch (c) {
+            case 'y':
+              i++;
+              j++;
+              break;
+            case 'm':
+              nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'd':
+              nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'H':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'h':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'M':
+              nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 's':
+              nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 't':
+              bPm = (j < value.GetLength() && value[j] == 'p');
+              i++;
+              j++;
+              break;
+          }
+        } else if (remaining == 1 || format[i + 2] != c) {
+          switch (c) {
+            case 'y':
+              nYear = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'm':
+              nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'd':
+              nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'H':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'h':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'M':
+              nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 's':
+              nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 't':
+              bPm = (j + 1 < value.GetLength() && value[j] == 'p' &&
+                     value[j + 1] == 'm');
+              i += 2;
+              j += 2;
+              break;
+          }
+        } else if (remaining == 2 || format[i + 3] != c) {
+          switch (c) {
+            case 'm': {
+              WideString sMonth = FX_ParseStringString(value, j, &nSkip);
+              bool bFind = false;
+              for (int m = 0; m < 12; m++) {
+                if (sMonth.CompareNoCase(kMonths[m]) == 0) {
+                  nMonth = m + 1;
+                  i += 3;
+                  j += nSkip;
+                  bFind = true;
+                  break;
+                }
+              }
+
+              if (!bFind) {
+                nMonth = FX_ParseStringInteger(value, j, &nSkip, 3);
+                i += 3;
+                j += nSkip;
+              }
+            } break;
+            case 'y':
+              break;
+            default:
+              i += 3;
+              j += 3;
+              break;
+          }
+        } else if (remaining == 3 || format[i + 4] != c) {
+          switch (c) {
+            case 'y':
+              nYear = FX_ParseStringInteger(value, j, &nSkip, 4);
+              j += nSkip;
+              i += 4;
+              break;
+            case 'm': {
+              bool bFind = false;
+
+              WideString sMonth = FX_ParseStringString(value, j, &nSkip);
+              sMonth.MakeLower();
+
+              for (int m = 0; m < 12; m++) {
+                WideString sFullMonths = WideString(kFullMonths[m]);
+                sFullMonths.MakeLower();
+
+                if (sFullMonths.Contains(sMonth.c_str())) {
+                  nMonth = m + 1;
+                  i += 4;
+                  j += nSkip;
+                  bFind = true;
+                  break;
+                }
+              }
+
+              if (!bFind) {
+                nMonth = FX_ParseStringInteger(value, j, &nSkip, 4);
+                i += 4;
+                j += nSkip;
+              }
+            } break;
+            default:
+              i += 4;
+              j += 4;
+              break;
+          }
+        } else {
+          if (j >= value.GetLength() || format[i] != value[j]) {
+            bBadFormat = true;
+            bExit = true;
+          }
+          i++;
+          j++;
+        }
+
+        if (oldj == j) {
+          bBadFormat = true;
+          bExit = true;
+        }
+        break;
+      }
+
+      default:
+        if (value.GetLength() <= j) {
+          bExit = true;
+        } else if (format[i] != value[j]) {
+          bBadFormat = true;
+          bExit = true;
+        }
+
+        i++;
+        j++;
+        break;
+    }
+  }
+
+  if (bBadFormat)
+    return ConversionStatus::kBadFormat;
+
+  if (bPm)
+    nHour += 12;
+
+  if (nYear >= 0 && nYear <= nYearSub)
+    nYear += 2000;
+
+  if (!FX_IsValidMonth(nMonth) || !FX_IsValidDay(nDay) ||
+      !FX_IsValid24Hour(nHour) || !FX_IsValidMinute(nMin) ||
+      !FX_IsValidSecond(nSec)) {
+    return ConversionStatus::kBadDate;
+  }
+
+  dt = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
+                   FX_MakeTime(nHour, nMin, nSec, 0));
+  if (std::isnan(dt))
+    return ConversionStatus::kBadDate;
+
+  *result = dt;
+  return ConversionStatus::kSuccess;
+}
+
 }  // namespace fxjs
diff --git a/fxjs/fx_date_helpers.h b/fxjs/fx_date_helpers.h
index 8c21df0..f92caba 100644
--- a/fxjs/fx_date_helpers.h
+++ b/fxjs/fx_date_helpers.h
@@ -13,6 +13,11 @@
 
 namespace fxjs {
 
+enum class ConversionStatus { kSuccess = 0, kBadFormat, kBadDate };
+
+extern const wchar_t* const kMonths[12];
+extern const wchar_t* const kFullMonths[12];
+
 double FX_GetDateTime();
 int FX_GetYearFromTime(double dt);
 int FX_GetMonthFromTime(double dt);
@@ -20,6 +25,11 @@
 int FX_GetHourFromTime(double dt);
 int FX_GetMinFromTime(double dt);
 int FX_GetSecFromTime(double dt);
+bool FX_IsValidMonth(int m);
+bool FX_IsValidDay(int d);
+bool FX_IsValid24Hour(int h);
+bool FX_IsValidMinute(int m);
+bool FX_IsValidSecond(int s);
 double FX_LocalTime(double d);
 double FX_MakeDay(int nYear, int nMonth, int nDay);
 double FX_MakeTime(int nHour, int nMin, int nSec, int nMs);
@@ -34,6 +44,10 @@
                                 size_t nStart,
                                 size_t* pSkip);
 
+ConversionStatus FX_ParseDateUsingFormat(const WideString& value,
+                                         const WideString& format,
+                                         double* result);
+
 }  // namespace fxjs
 
 using fxjs::FX_GetDateTime;
@@ -43,11 +57,17 @@
 using fxjs::FX_GetHourFromTime;
 using fxjs::FX_GetMinFromTime;
 using fxjs::FX_GetSecFromTime;
+using fxjs::FX_IsValidMonth;
+using fxjs::FX_IsValidDay;
+using fxjs::FX_IsValid24Hour;
+using fxjs::FX_IsValidMinute;
+using fxjs::FX_IsValidSecond;
 using fxjs::FX_LocalTime;
 using fxjs::FX_MakeDay;
 using fxjs::FX_MakeTime;
 using fxjs::FX_MakeDate;
 using fxjs::FX_ParseStringInteger;
 using fxjs::FX_ParseStringString;
+using fxjs::FX_ParseDateUsingFormat;
 
 #endif  // FXJS_FX_DATE_HELPERS_H_