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_