Add FXSYS_wcsftime() to avoid termination on win.

Bug: 712725
Change-Id: I3384385176410964a87b12c2587f68247ea80cc9
Reviewed-on: https://pdfium-review.googlesource.com/4454
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 23e1cf2..3e5eafb 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -782,6 +782,7 @@
     "core/fxcrt/fx_stream.cpp",
     "core/fxcrt/fx_stream.h",
     "core/fxcrt/fx_string.h",
+    "core/fxcrt/fx_system.cpp",
     "core/fxcrt/fx_system.h",
     "core/fxcrt/fx_ucd.h",
     "core/fxcrt/fx_ucddata.cpp",
diff --git a/core/fxcrt/fx_system.cpp b/core/fxcrt/fx_system.cpp
new file mode 100644
index 0000000..d2b04f5
--- /dev/null
+++ b/core/fxcrt/fx_system.cpp
@@ -0,0 +1,28 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/fx_system.h"
+
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+
+size_t FXSYS_wcsftime(wchar_t* strDest,
+                      size_t maxsize,
+                      const wchar_t* format,
+                      const struct tm* timeptr) {
+  // Avoid tripping an invalid parameter handler and crashing process.
+  // Note: leap seconds may cause tm_sec == 60.
+  if (timeptr->tm_year < 0 || timeptr->tm_mon < 0 || timeptr->tm_mon > 11 ||
+      timeptr->tm_mday < 1 || timeptr->tm_mday > 31 || timeptr->tm_hour < 0 ||
+      timeptr->tm_hour > 23 || timeptr->tm_min < 0 || timeptr->tm_min > 59 ||
+      timeptr->tm_sec < 0 || timeptr->tm_sec > 60 || timeptr->tm_wday < 0 ||
+      timeptr->tm_wday > 6 || timeptr->tm_yday < 0 || timeptr->tm_yday > 365) {
+    strDest[0] = L'\0';
+    return 0;
+  }
+  return wcsftime(strDest, maxsize, format, timeptr);
+}
+
+#endif  // _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
diff --git a/core/fxcrt/fx_system.h b/core/fxcrt/fx_system.h
index 1ee0d8b..1e66e7b 100644
--- a/core/fxcrt/fx_system.h
+++ b/core/fxcrt/fx_system.h
@@ -114,7 +114,7 @@
                     _Printf_format_string_ const char* fmt,
                     ...);
 void FXSYS_vsnprintf(char* str, size_t size, const char* fmt, va_list ap);
-#else
+#else  // _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ && _MSC_VER < 1900
 #define FXSYS_snprintf (void)snprintf
 #define FXSYS_vsnprintf (void)vsnprintf
 #endif  // _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ && _MSC_VER < 1900
@@ -180,7 +180,10 @@
 #define FXSYS_pow(a, b) (float)powf(a, b)
 #define FXSYS_GetFullPathName GetFullPathName
 #define FXSYS_GetModuleFileName GetModuleFileName
-
+size_t FXSYS_wcsftime(wchar_t* strDest,
+                      size_t maxsize,
+                      const wchar_t* format,
+                      const struct tm* timeptr);
 #ifdef _NATIVE_WCHAR_T_DEFINED
 #define FXSYS_wcsicmp(str1, str2) _wcsicmp((wchar_t*)(str1), (wchar_t*)(str2))
 #define FXSYS_WideCharToMultiByte(p1, p2, p3, p4, p5, p6, p7, p8) \
@@ -189,14 +192,14 @@
   MultiByteToWideChar(p1, p2, p3, p4, (wchar_t*)(p5), p6)
 #define FXSYS_wcslwr(str) _wcslwr((wchar_t*)(str))
 #define FXSYS_wcsupr(str) _wcsupr((wchar_t*)(str))
-#else
+#else  // _NATIVE_WCHAR_T_DEFINED
 #define FXSYS_wcsicmp _wcsicmp
 #define FXSYS_WideCharToMultiByte WideCharToMultiByte
 #define FXSYS_MultiByteToWideChar MultiByteToWideChar
 #define FXSYS_wcslwr _wcslwr
 #define FXSYS_wcsupr _wcsupr
 #endif  // _NATIVE_WCHAR_T_DEFINED
-#else
+#else   // _FXM_PLATFORM == _FXM_PLATFORM_WINDOWS_
 int FXSYS_GetACP();
 char* FXSYS_itoa(int value, char* str, int radix);
 int FXSYS_WideCharToMultiByte(uint32_t codepage,
@@ -225,6 +228,7 @@
 wchar_t* FXSYS_wcslwr(wchar_t* str);
 wchar_t* FXSYS_wcsupr(wchar_t* str);
 #define FXSYS_pow(a, b) (float)pow(a, b)
+#define FXSYS_wcsftime wcsftime
 #endif  // _FXM_PLATFORM == _FXM_PLATFORM_WINDOWS_
 
 #define FXDWORD_GET_LSBFIRST(p)                                                \
@@ -249,7 +253,6 @@
 //   size_t size;
 //   printf("xyz: %" PRIuS, size);
 // The "u" in the macro corresponds to %u, and S is for "size".
-
 #if _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
 
 #if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
diff --git a/core/fxcrt/fx_system_unittest.cpp b/core/fxcrt/fx_system_unittest.cpp
index def0043..5a7660d 100644
--- a/core/fxcrt/fx_system_unittest.cpp
+++ b/core/fxcrt/fx_system_unittest.cpp
@@ -160,3 +160,40 @@
 }
 
 #endif  // _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
+
+TEST(fxcrt, FXSYS_wcsftime) {
+  struct tm good_time = {};
+  good_time.tm_year = 74;  // 1900-based.
+  good_time.tm_mon = 7;    // 0-based.
+  good_time.tm_mday = 9;   // 1-based.
+  good_time.tm_hour = 11;
+  good_time.tm_min = 59;
+  good_time.tm_sec = 59;
+
+  wchar_t buf[100] = {};
+  EXPECT_EQ(19u, FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%Y-%m-%dT%H:%M:%S",
+                                &good_time));
+  EXPECT_EQ(std::wstring(L"1974-08-09T11:59:59"), buf);
+
+  // Ensure wcsftime handles bad years, etc. without crashing.
+  struct tm bad_time = {};
+  bad_time.tm_year = -1;
+  bad_time.tm_mon = -1;
+  bad_time.tm_mday = -1;
+  bad_time.tm_hour = -1;
+  bad_time.tm_min = -1;
+  bad_time.tm_sec = -1;
+
+  FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%y-%m-%dT%H:%M:%S", &bad_time);
+
+  // Ensure wcsftime handles bad-ish day without crashing (Feb 30).
+  struct tm feb_time = {};
+  feb_time.tm_year = 115;  // 1900-based.
+  feb_time.tm_mon = 1;     // 0-based.
+  feb_time.tm_mday = 30;   // 1-based.
+  feb_time.tm_hour = 12;
+  feb_time.tm_min = 00;
+  feb_time.tm_sec = 00;
+
+  FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%y-%m-%dT%H:%M:%S", &feb_time);
+}