Fix timezone inconsistency in document methods test.

We do this by adding an override that forces GM time on
everyone when run from the test harness.

Generalize presubmit warnings so that the new function passes.
De-duplicate lambda capture in place of static function.

Change-Id: I15b34bea558baf1763476b36f0bca76614984107
Reviewed-on: https://pdfium-review.googlesource.com/c/44390
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/fx_extension.cpp b/core/fxcrt/fx_extension.cpp
index 8e78e4d..443e301 100644
--- a/core/fxcrt/fx_extension.cpp
+++ b/core/fxcrt/fx_extension.cpp
@@ -12,7 +12,20 @@
 
 #include "third_party/base/compiler_specific.h"
 
-time_t (*time_func)() = []() -> time_t { return time(nullptr); };
+namespace {
+
+time_t DefaultTimeFunction() {
+  return time(nullptr);
+}
+
+struct tm* DefaultLocaltimeFunction(const time_t* tp) {
+  return localtime(tp);
+}
+
+time_t (*g_time_func)() = DefaultTimeFunction;
+struct tm* (*g_localtime_func)(const time_t*) = DefaultLocaltimeFunction;
+
+}  // namespace
 
 float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen) {
   ASSERT(pwsStr);
@@ -171,12 +184,20 @@
 }
 
 void FXSYS_SetTimeFunction(time_t (*func)()) {
-  time_func = func ? func : []() -> time_t { return time(nullptr); };
+  g_time_func = func ? func : DefaultTimeFunction;
+}
+
+void FXSYS_SetLocaltimeFunction(struct tm* (*func)(const time_t*)) {
+  g_localtime_func = func ? func : DefaultLocaltimeFunction;
 }
 
 time_t FXSYS_time(time_t* tloc) {
-  time_t ret_val = time_func();
+  time_t ret_val = g_time_func();
   if (tloc)
     *tloc = ret_val;
   return ret_val;
 }
+
+struct tm* FXSYS_localtime(const time_t* tp) {
+  return g_localtime_func(tp);
+}
diff --git a/core/fxcrt/fx_extension.h b/core/fxcrt/fx_extension.h
index c1cd188..38f9fd5 100644
--- a/core/fxcrt/fx_extension.h
+++ b/core/fxcrt/fx_extension.h
@@ -7,6 +7,8 @@
 #ifndef CORE_FXCRT_FX_EXTENSION_H_
 #define CORE_FXCRT_FX_EXTENSION_H_
 
+#include <time.h>
+
 #include <cctype>
 #include <cmath>
 #include <cwctype>
@@ -106,7 +108,12 @@
   return lhs < rhs;
 }
 
+// Override time/localtime functions for test consistency.
 void FXSYS_SetTimeFunction(time_t (*func)());
+void FXSYS_SetLocaltimeFunction(struct tm* (*func)(const time_t*));
+
+// Replacements for time/localtime that respect overrides.
 time_t FXSYS_time(time_t* tloc);
+struct tm* FXSYS_localtime(const time_t* tp);
 
 #endif  // CORE_FXCRT_FX_EXTENSION_H_
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index dcb3d5e..585e7e1 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -164,7 +164,7 @@
   ByteString DateStr;
   if (FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS)) {
     if (FXSYS_time(&currentTime) != -1) {
-      tm* pTM = localtime(&currentTime);
+      tm* pTM = FXSYS_localtime(&currentTime);
       if (pTM) {
         DateStr = ByteString::Format(
             "D:%04d%02d%02d%02d%02d%02d", pTM->tm_year + 1900, pTM->tm_mon + 1,
diff --git a/fpdfsdk/fpdf_ext.cpp b/fpdfsdk/fpdf_ext.cpp
index 1ff27cc..9f7b24b 100644
--- a/fpdfsdk/fpdf_ext.cpp
+++ b/fpdfsdk/fpdf_ext.cpp
@@ -83,6 +83,11 @@
   FXSYS_SetTimeFunction(func);
 }
 
+FPDF_EXPORT void FPDF_CALLCONV
+FSDK_SetLocaltimeFunction(struct tm* (*func)(const time_t* tp)) {
+  FXSYS_SetLocaltimeFunction(func);
+}
+
 FPDF_EXPORT int FPDF_CALLCONV FPDFDoc_GetPageMode(FPDF_DOCUMENT document) {
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
   if (!pDoc)
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index ab184cc..9cf0376 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -216,6 +216,7 @@
 
     // fpdf_ext.h
     CHK(FPDFDoc_GetPageMode);
+    CHK(FSDK_SetLocaltimeFunction);
     CHK(FSDK_SetTimeFunction);
     CHK(FSDK_SetUnSpObjProcessHandler);
 
diff --git a/fxjs/cfxjse_formcalc_context.cpp b/fxjs/cfxjse_formcalc_context.cpp
index f95ee83..d77453e 100644
--- a/fxjs/cfxjse_formcalc_context.cpp
+++ b/fxjs/cfxjse_formcalc_context.cpp
@@ -959,7 +959,7 @@
   FXSYS_time(&now);
 
   struct tm* pGmt = gmtime(&now);
-  struct tm* pLocal = localtime(&now);
+  struct tm* pLocal = FXSYS_localtime(&now);
   *pHour = pLocal->tm_hour - pGmt->tm_hour;
   *pMin = pLocal->tm_min - pGmt->tm_min;
   *pSec = pLocal->tm_sec - pGmt->tm_sec;
diff --git a/fxjs/js_define.cpp b/fxjs/js_define.cpp
index 54bd8e1..b4e0197 100644
--- a/fxjs/js_define.cpp
+++ b/fxjs/js_define.cpp
@@ -24,7 +24,7 @@
     return 0;
   time_t t = 0;
   FXSYS_time(&t);
-  localtime(&t);
+  FXSYS_localtime(&t);
 #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
   // In gcc 'timezone' is a global variable declared in time.h. In VC++, that
   // variable was removed in VC++ 2015, with _get_timezone replacing it.
@@ -38,7 +38,7 @@
   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
     return 0;
   time_t t = (time_t)(d / 1000);
-  struct tm* tmp = localtime(&t);
+  struct tm* tmp = FXSYS_localtime(&t);
   if (!tmp)
     return 0;
   if (tmp->tm_isdst > 0)
@@ -175,12 +175,10 @@
 double JS_GetDateTime() {
   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
     return 0;
+
   time_t t = FXSYS_time(nullptr);
-  struct tm* pTm = localtime(&t);
-
-  int year = pTm->tm_year + 1900;
-  double t1 = TimeFromYear(year);
-
+  struct tm* pTm = FXSYS_localtime(&t);
+  double t1 = TimeFromYear(pTm->tm_year + 1900);
   return t1 + pTm->tm_yday * 86400000.0 + pTm->tm_hour * 3600000.0 +
          pTm->tm_min * 60000.0 + pTm->tm_sec * 1000.0;
 }
diff --git a/public/fpdf_ext.h b/public/fpdf_ext.h
index a531f14..b1784dd 100644
--- a/public/fpdf_ext.h
+++ b/public/fpdf_ext.h
@@ -69,14 +69,25 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FSDK_SetUnSpObjProcessHandler(UNSUPPORT_INFO* unsp_info);
 
-// Sets generator function for calls to time.
+// Set replacement function for calls to time().
 //
 // This API is intended to be used only for testing, thus may cause PDFium to
 // behave poorly in production environments.
 //
-//   func - Function pointer to alternate implementation of time.
+//   func - Function pointer to alternate implementation of time(), or
+//          NULL to restore to actual time() call itself.
 FPDF_EXPORT void FPDF_CALLCONV FSDK_SetTimeFunction(time_t (*func)());
 
+// Set replacement function for calls to localtime().
+//
+// This API is intended to be used only for testing, thus may cause PDFium to
+// behave poorly in production environments.
+//
+//   func - Function pointer to alternate implementation of localtime(), or
+//          NULL to restore to actual localtime() call itself.
+FPDF_EXPORT void FPDF_CALLCONV
+FSDK_SetLocaltimeFunction(struct tm* (*func)(const time_t*));
+
 // Unknown page mode.
 #define PAGEMODE_UNKNOWN -1
 // Document outline, and thumbnails hidden.
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index ac24bcc..6f70c34 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -963,7 +963,8 @@
     // This must be a static var to avoid explicit capture, so the lambda can be
     // converted to a function ptr.
     static time_t time_ret = options.time;
-    FSDK_SetTimeFunction([]() -> time_t { return time_ret; });
+    FSDK_SetTimeFunction([]() { return time_ret; });
+    FSDK_SetLocaltimeFunction([](const time_t* tp) { return gmtime(tp); });
   }
 
   for (const std::string& filename : files) {
diff --git a/testing/resources/javascript/public_methods.in b/testing/resources/javascript/public_methods.in
index 4b70375..0779767 100644
--- a/testing/resources/javascript/public_methods.in
+++ b/testing/resources/javascript/public_methods.in
@@ -166,7 +166,7 @@
 
       expectError(undefined, "AFParseDateEx()");
       expectError(undefined, "AFParseDateEx(1, 2, 3)");
-      expect(undefined, "AFParseDateEx(1, 2)", "1399646930000");
+      expect(undefined, "AFParseDateEx(1, 2)", "1399672130000");
 
       app.alert("**********************");
 
@@ -223,7 +223,7 @@
 
       expectError(undefined, "AFTime_Format()");
       expectError(undefined, "AFTime_Format(1, 2)");
-      expectEventValue(undefined, "AFTime_Format(1)", "2:48 pm");
+      expectEventValue(undefined, "AFTime_Format(1)", "9:48 pm");
 
       app.alert("**********************");
 
diff --git a/testing/resources/javascript/public_methods_expected.txt b/testing/resources/javascript/public_methods_expected.txt
index 12e8496..48928f5 100644
--- a/testing/resources/javascript/public_methods_expected.txt
+++ b/testing/resources/javascript/public_methods_expected.txt
@@ -55,7 +55,7 @@
 Alert: **********************
 Alert: PASS: AFParseDateEx() threw AFParseDateEx: Incorrect number of parameters passed to function.
 Alert: PASS: AFParseDateEx(1, 2, 3) threw AFParseDateEx: Incorrect number of parameters passed to function.
-Alert: PASS: AFParseDateEx(1, 2) = 1399646930000
+Alert: PASS: AFParseDateEx(1, 2) = 1399672130000
 Alert: **********************
 Alert: PASS: AFPercent_Format() threw AFPercent_Format: Incorrect number of parameters passed to function.
 Alert: PASS: AFPercent_Format(1, 2, 3) threw AFPercent_Format: Incorrect number of parameters passed to function.
@@ -96,7 +96,7 @@
 Alert: **********************
 Alert: PASS: AFTime_Format() threw AFTime_Format: Incorrect number of parameters passed to function.
 Alert: PASS: AFTime_Format(1, 2) threw AFTime_Format: Incorrect number of parameters passed to function.
-Alert: PASS: AFTime_Format(1) = 2:48 pm
+Alert: PASS: AFTime_Format(1) = 9:48 pm
 Alert: **********************
 Alert: PASS: AFTime_FormatEx() threw AFTime_FormatEx: Incorrect number of parameters passed to function.
 Alert: PASS: AFTime_FormatEx('blooey', 42) threw AFTime_FormatEx: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/util_printd_expected.txt b/testing/resources/javascript/util_printd_expected.txt
index 2f077fc..c609e34 100644
--- a/testing/resources/javascript/util_printd_expected.txt
+++ b/testing/resources/javascript/util_printd_expected.txt
@@ -1,7 +1,7 @@
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 15:59:58
-Alert: 0: D:20140704155958
-Alert: 1: 2014.07.04 15:59:58
-Alert: 2: 2014/07/04 15:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 14:59:58
+Alert: 0: D:20140704145958
+Alert: 1: 2014.07.04 14:59:58
+Alert: 2: 2014/07/04 14:59:58
 Alert: 3: Caught error: util.printd: Incorrect parameter value.
 Alert: mmmm: July
 Alert: mmm: Jul
@@ -13,9 +13,9 @@
 Alert: d: 4
 Alert: yyyy: 2014
 Alert: yy: 14
-Alert: HH: 15
-Alert: H: 15
-Alert: hh: 03
+Alert: HH: 14
+Alert: H: 14
+Alert: hh: 02
 Alert: MM: 59
 Alert: M: 59
 Alert: ss: 58
@@ -23,7 +23,7 @@
 Alert: t: t
 Alert: abc.efg.i.kl.nopqr..uvwxyzABC.EFG.I.KL.NOPQR..UVWXYZ0123456780: abc.efg.i.kl.nopqr..uvwxyzABC.EFG.I.KL.NOPQR..UVWXYZ0123456780
 Alert: !@#$^&*()-_<>[];:~: !@#$^&*()-_<>[];:~
-Alert: %z %d %%z %%d %%%z %%%d %%% hh:MM: z 4 z 4 z 4  03:59
+Alert: %z %d %%z %%d %%%z %%%d %%% hh:MM: z 4 z 4 z 4  02:59
 Alert: : 
 Alert: mm/dd/yyyy: 07/04/2014
 Alert: mm/dd/yyyy: 12/31/1849
@@ -39,16 +39,16 @@
 Alert: mm: 07
 Alert: mm: Caught error: util.printd: Operation not supported.
 Alert: mm/dd/yyyy HH:MM:ss: 07/03/1900 15:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 09/04/2015 15:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 09/04/2015 14:59:58
 Alert: mm/dd/yyyy HH:MM:ss: 12/09/2015 15:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/06/2014 02:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/05/2014 11:34:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 16:00:05
-Alert: mm/dd/yyyy HH:MM:ss: 05/01/2014 15:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/06/2014 01:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/05/2014 10:34:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 15:00:05
+Alert: mm/dd/yyyy HH:MM:ss: 05/01/2014 14:59:58
 Alert: mm/dd/yyyy HH:MM:ss: 03/02/2014 15:59:58
 Alert: mm/dd/yyyy HH:MM:ss: Caught error: util.printd: Incorrect parameter value.
 Alert: mm/dd/yyyy HH:MM:ss: 12/30/2013 15:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 06/29/2014 15:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/03/2014 23:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 14:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 15:58:59
+Alert: mm/dd/yyyy HH:MM:ss: 06/29/2014 14:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/03/2014 22:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 13:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 14:58:59
diff --git a/testing/tools/api_check.py b/testing/tools/api_check.py
index 934577b..da2869f 100755
--- a/testing/tools/api_check.py
+++ b/testing/tools/api_check.py
@@ -20,9 +20,7 @@
 def _IsValidFunctionName(function, filename):
   if function.startswith('FPDF'):
     return True
-  if function == 'FSDK_SetUnSpObjProcessHandler' and filename == 'fpdf_ext.h':
-    return True
-  if function == 'FSDK_SetTimeFunction' and filename == 'fpdf_ext.h':
+  if function.startswith('FSDK_') and filename == 'fpdf_ext.h':
     return True
   if function.startswith('FORM_') and filename == 'fpdf_formfill.h':
     return True