1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "fxjs/fx_date_helpers.h"
8 
9 #include <time.h>
10 
11 #include <cmath>
12 
13 #include "build/build_config.h"
14 #include "core/fxcrt/fx_extension.h"
15 #include "core/fxcrt/fx_system.h"
16 #include "fpdfsdk/cpdfsdk_helpers.h"
17 
18 namespace fxjs {
19 namespace {
20 
21 constexpr uint16_t daysMonth[12] = {0,   31,  59,  90,  120, 151,
22                                     181, 212, 243, 273, 304, 334};
23 constexpr uint16_t leapDaysMonth[12] = {0,   31,  60,  91,  121, 152,
24                                         182, 213, 244, 274, 305, 335};
25 
Mod(double x,double y)26 double Mod(double x, double y) {
27   double r = fmod(x, y);
28   if (r < 0)
29     r += y;
30   return r;
31 }
32 
GetLocalTZA()33 double GetLocalTZA() {
34   if (!IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
35     return 0;
36   time_t t = 0;
37   FXSYS_time(&t);
38 #ifdef __FreeBSD__
39   struct tm lt;
40   localtime_r(&t, &lt);
41   return (double)(-(lt.tm_gmtoff * 1000));
42 #else
43   FXSYS_localtime(&t);
44 #if defined(OS_WIN)
45   // In gcc 'timezone' is a global variable declared in time.h. In VC++, that
46   // variable was removed in VC++ 2015, with _get_timezone replacing it.
47   long timezone = 0;
48   _get_timezone(&timezone);
49 #endif
50   return (double)(-(timezone * 1000));
51 #endif // __FreeBSD__
52 }
53 
GetDaylightSavingTA(double d)54 int GetDaylightSavingTA(double d) {
55   if (!IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
56     return 0;
57   time_t t = (time_t)(d / 1000);
58   struct tm* tmp = FXSYS_localtime(&t);
59   if (!tmp)
60     return 0;
61   if (tmp->tm_isdst > 0)
62     // One hour.
63     return (int)60 * 60 * 1000;
64   return 0;
65 }
66 
IsLeapYear(int year)67 bool IsLeapYear(int year) {
68   return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 != 0));
69 }
70 
DayFromYear(int y)71 int DayFromYear(int y) {
72   return (int)(365 * (y - 1970.0) + floor((y - 1969.0) / 4) -
73                floor((y - 1901.0) / 100) + floor((y - 1601.0) / 400));
74 }
75 
TimeFromYear(int y)76 double TimeFromYear(int y) {
77   return 86400000.0 * DayFromYear(y);
78 }
79 
TimeFromYearMonth(int y,int m)80 double TimeFromYearMonth(int y, int m) {
81   const uint16_t* pMonth = IsLeapYear(y) ? leapDaysMonth : daysMonth;
82   return TimeFromYear(y) + ((double)pMonth[m]) * 86400000;
83 }
84 
Day(double t)85 int Day(double t) {
86   return static_cast<int>(floor(t / 86400000.0));
87 }
88 
YearFromTime(double t)89 int YearFromTime(double t) {
90   // estimate the time.
91   int y = 1970 + static_cast<int>(t / (365.2425 * 86400000.0));
92   if (TimeFromYear(y) <= t) {
93     while (TimeFromYear(y + 1) <= t)
94       y++;
95   } else {
96     while (TimeFromYear(y) > t)
97       y--;
98   }
99   return y;
100 }
101 
DayWithinYear(double t)102 int DayWithinYear(double t) {
103   int year = YearFromTime(t);
104   int day = Day(t);
105   return day - DayFromYear(year);
106 }
107 
MonthFromTime(double t)108 int MonthFromTime(double t) {
109   // Check for negative |day| values and check for January.
110   int day = DayWithinYear(t);
111   if (day < 0)
112     return -1;
113   if (day < 31)
114     return 0;
115 
116   if (IsLeapYear(YearFromTime(t)))
117     --day;
118 
119   // Check for February onwards.
120   static constexpr int kCumulativeDaysInMonths[] = {
121       59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
122   for (size_t i = 0; i < FX_ArraySize(kCumulativeDaysInMonths); ++i) {
123     if (day < kCumulativeDaysInMonths[i])
124       return i + 1;
125   }
126 
127   return -1;
128 }
129 
DateFromTime(double t)130 int DateFromTime(double t) {
131   int day = DayWithinYear(t);
132   int year = YearFromTime(t);
133   int leap = IsLeapYear(year);
134   int month = MonthFromTime(t);
135   switch (month) {
136     case 0:
137       return day + 1;
138     case 1:
139       return day - 30;
140     case 2:
141       return day - 58 - leap;
142     case 3:
143       return day - 89 - leap;
144     case 4:
145       return day - 119 - leap;
146     case 5:
147       return day - 150 - leap;
148     case 6:
149       return day - 180 - leap;
150     case 7:
151       return day - 211 - leap;
152     case 8:
153       return day - 242 - leap;
154     case 9:
155       return day - 272 - leap;
156     case 10:
157       return day - 303 - leap;
158     case 11:
159       return day - 333 - leap;
160     default:
161       return 0;
162   }
163 }
164 
FindSubWordLength(const WideString & str,size_t nStart)165 size_t FindSubWordLength(const WideString& str, size_t nStart) {
166   pdfium::span<const wchar_t> data = str.span();
167   size_t i = nStart;
168   while (i < data.size() && std::iswalnum(data[i]))
169     ++i;
170   return i - nStart;
171 }
172 
173 }  // namespace
174 
175 const wchar_t* const kMonths[12] = {L"Jan", L"Feb", L"Mar", L"Apr",
176                                     L"May", L"Jun", L"Jul", L"Aug",
177                                     L"Sep", L"Oct", L"Nov", L"Dec"};
178 
179 const wchar_t* const kFullMonths[12] = {L"January", L"February", L"March",
180                                         L"April",   L"May",      L"June",
181                                         L"July",    L"August",   L"September",
182                                         L"October", L"November", L"December"};
183 
184 static constexpr size_t KMonthAbbreviationLength = 3;  // Anything in |kMonths|.
185 static constexpr size_t kLongestFullMonthLength = 9;   // September
186 
FX_GetDateTime()187 double FX_GetDateTime() {
188   if (!IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
189     return 0;
190 
191   time_t t = FXSYS_time(nullptr);
192   struct tm* pTm = FXSYS_localtime(&t);
193   double t1 = TimeFromYear(pTm->tm_year + 1900);
194   return t1 + pTm->tm_yday * 86400000.0 + pTm->tm_hour * 3600000.0 +
195          pTm->tm_min * 60000.0 + pTm->tm_sec * 1000.0;
196 }
197 
FX_GetYearFromTime(double dt)198 int FX_GetYearFromTime(double dt) {
199   return YearFromTime(dt);
200 }
201 
FX_GetMonthFromTime(double dt)202 int FX_GetMonthFromTime(double dt) {
203   return MonthFromTime(dt);
204 }
205 
FX_GetDayFromTime(double dt)206 int FX_GetDayFromTime(double dt) {
207   return DateFromTime(dt);
208 }
209 
FX_GetHourFromTime(double dt)210 int FX_GetHourFromTime(double dt) {
211   return (int)Mod(floor(dt / (60 * 60 * 1000)), 24);
212 }
213 
FX_GetMinFromTime(double dt)214 int FX_GetMinFromTime(double dt) {
215   return (int)Mod(floor(dt / (60 * 1000)), 60);
216 }
217 
FX_GetSecFromTime(double dt)218 int FX_GetSecFromTime(double dt) {
219   return (int)Mod(floor(dt / 1000), 60);
220 }
221 
FX_IsValidMonth(int m)222 bool FX_IsValidMonth(int m) {
223   return m >= 1 && m <= 12;
224 }
225 
226 // TODO(thestig): Should this take the month into consideration?
FX_IsValidDay(int d)227 bool FX_IsValidDay(int d) {
228   return d >= 1 && d <= 31;
229 }
230 
231 // TODO(thestig): Should 24 be allowed? Similarly, 60 for minutes and seconds.
FX_IsValid24Hour(int h)232 bool FX_IsValid24Hour(int h) {
233   return h >= 0 && h <= 24;
234 }
235 
FX_IsValidMinute(int m)236 bool FX_IsValidMinute(int m) {
237   return m >= 0 && m <= 60;
238 }
239 
FX_IsValidSecond(int s)240 bool FX_IsValidSecond(int s) {
241   return s >= 0 && s <= 60;
242 }
243 
FX_LocalTime(double d)244 double FX_LocalTime(double d) {
245   return d + GetLocalTZA() + GetDaylightSavingTA(d);
246 }
247 
FX_MakeDay(int nYear,int nMonth,int nDate)248 double FX_MakeDay(int nYear, int nMonth, int nDate) {
249   double y = static_cast<double>(nYear);
250   double m = static_cast<double>(nMonth);
251   double dt = static_cast<double>(nDate);
252   double ym = y + floor(m / 12);
253   double mn = Mod(m, 12);
254   double t = TimeFromYearMonth(static_cast<int>(ym), static_cast<int>(mn));
255   if (YearFromTime(t) != ym || MonthFromTime(t) != mn || DateFromTime(t) != 1)
256     return std::nan("");
257 
258   return Day(t) + dt - 1;
259 }
260 
FX_MakeTime(int nHour,int nMin,int nSec,int nMs)261 double FX_MakeTime(int nHour, int nMin, int nSec, int nMs) {
262   double h = static_cast<double>(nHour);
263   double m = static_cast<double>(nMin);
264   double s = static_cast<double>(nSec);
265   double milli = static_cast<double>(nMs);
266   return h * 3600000 + m * 60000 + s * 1000 + milli;
267 }
268 
FX_MakeDate(double day,double time)269 double FX_MakeDate(double day, double time) {
270   if (!std::isfinite(day) || !std::isfinite(time))
271     return std::nan("");
272 
273   return day * 86400000 + time;
274 }
275 
FX_ParseStringInteger(const WideString & str,size_t nStart,size_t * pSkip,size_t nMaxStep)276 int FX_ParseStringInteger(const WideString& str,
277                           size_t nStart,
278                           size_t* pSkip,
279                           size_t nMaxStep) {
280   int nRet = 0;
281   size_t nSkip = 0;
282   for (size_t i = nStart; i < str.GetLength(); ++i) {
283     if (i - nStart > 10)
284       break;
285 
286     wchar_t c = str[i];
287     if (!FXSYS_IsDecimalDigit(c))
288       break;
289 
290     nRet = nRet * 10 + FXSYS_DecimalCharToInt(c);
291     ++nSkip;
292     if (nSkip >= nMaxStep)
293       break;
294   }
295 
296   *pSkip = nSkip;
297   return nRet;
298 }
299 
FX_ParseDateUsingFormat(const WideString & value,const WideString & format,double * result)300 ConversionStatus FX_ParseDateUsingFormat(const WideString& value,
301                                          const WideString& format,
302                                          double* result) {
303   double dt = FX_GetDateTime();
304   if (format.IsEmpty() || value.IsEmpty()) {
305     *result = dt;
306     return ConversionStatus::kSuccess;
307   }
308 
309   int nYear = FX_GetYearFromTime(dt);
310   int nMonth = FX_GetMonthFromTime(dt) + 1;
311   int nDay = FX_GetDayFromTime(dt);
312   int nHour = FX_GetHourFromTime(dt);
313   int nMin = FX_GetMinFromTime(dt);
314   int nSec = FX_GetSecFromTime(dt);
315   int nYearSub = 99;  // nYear - 2000;
316   bool bPm = false;
317   bool bExit = false;
318   bool bBadFormat = false;
319   size_t i = 0;
320   size_t j = 0;
321 
322   while (i < format.GetLength()) {
323     if (bExit)
324       break;
325 
326     wchar_t c = format[i];
327     switch (c) {
328       case ':':
329       case '.':
330       case '-':
331       case '\\':
332       case '/':
333         i++;
334         j++;
335         break;
336 
337       case 'y':
338       case 'm':
339       case 'd':
340       case 'H':
341       case 'h':
342       case 'M':
343       case 's':
344       case 't': {
345         size_t oldj = j;
346         size_t nSkip = 0;
347         size_t remaining = format.GetLength() - i - 1;
348 
349         if (remaining == 0 || format[i + 1] != c) {
350           switch (c) {
351             case 'y':
352               i++;
353               j++;
354               break;
355             case 'm':
356               nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
357               i++;
358               j += nSkip;
359               break;
360             case 'd':
361               nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
362               i++;
363               j += nSkip;
364               break;
365             case 'H':
366               nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
367               i++;
368               j += nSkip;
369               break;
370             case 'h':
371               nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
372               i++;
373               j += nSkip;
374               break;
375             case 'M':
376               nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
377               i++;
378               j += nSkip;
379               break;
380             case 's':
381               nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
382               i++;
383               j += nSkip;
384               break;
385             case 't':
386               bPm = (j < value.GetLength() && value[j] == 'p');
387               i++;
388               j++;
389               break;
390           }
391         } else if (remaining == 1 || format[i + 2] != c) {
392           switch (c) {
393             case 'y':
394               nYear = FX_ParseStringInteger(value, j, &nSkip, 2);
395               i += 2;
396               j += nSkip;
397               break;
398             case 'm':
399               nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
400               i += 2;
401               j += nSkip;
402               break;
403             case 'd':
404               nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
405               i += 2;
406               j += nSkip;
407               break;
408             case 'H':
409               nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
410               i += 2;
411               j += nSkip;
412               break;
413             case 'h':
414               nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
415               i += 2;
416               j += nSkip;
417               break;
418             case 'M':
419               nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
420               i += 2;
421               j += nSkip;
422               break;
423             case 's':
424               nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
425               i += 2;
426               j += nSkip;
427               break;
428             case 't':
429               bPm = (j + 1 < value.GetLength() && value[j] == 'p' &&
430                      value[j + 1] == 'm');
431               i += 2;
432               j += 2;
433               break;
434           }
435         } else if (remaining == 2 || format[i + 3] != c) {
436           switch (c) {
437             case 'm': {
438               bool bFind = false;
439               nSkip = FindSubWordLength(value, j);
440               if (nSkip == KMonthAbbreviationLength) {
441                 WideString sMonth = value.Substr(j, KMonthAbbreviationLength);
442                 for (size_t m = 0; m < FX_ArraySize(kMonths); ++m) {
443                   if (sMonth.CompareNoCase(kMonths[m]) == 0) {
444                     nMonth = m + 1;
445                     i += 3;
446                     j += nSkip;
447                     bFind = true;
448                     break;
449                   }
450                 }
451               }
452 
453               if (!bFind) {
454                 nMonth = FX_ParseStringInteger(value, j, &nSkip, 3);
455                 i += 3;
456                 j += nSkip;
457               }
458             } break;
459             case 'y':
460               break;
461             default:
462               i += 3;
463               j += 3;
464               break;
465           }
466         } else if (remaining == 3 || format[i + 4] != c) {
467           switch (c) {
468             case 'y':
469               nYear = FX_ParseStringInteger(value, j, &nSkip, 4);
470               j += nSkip;
471               i += 4;
472               break;
473             case 'm': {
474               bool bFind = false;
475               nSkip = FindSubWordLength(value, j);
476               if (nSkip <= kLongestFullMonthLength) {
477                 WideString sMonth = value.Substr(j, nSkip);
478                 sMonth.MakeLower();
479                 for (size_t m = 0; m < FX_ArraySize(kFullMonths); ++m) {
480                   WideString sFullMonths = WideString(kFullMonths[m]);
481                   sFullMonths.MakeLower();
482                   if (sFullMonths.Contains(sMonth.c_str())) {
483                     nMonth = m + 1;
484                     i += 4;
485                     j += nSkip;
486                     bFind = true;
487                     break;
488                   }
489                 }
490               }
491 
492               if (!bFind) {
493                 nMonth = FX_ParseStringInteger(value, j, &nSkip, 4);
494                 i += 4;
495                 j += nSkip;
496               }
497             } break;
498             default:
499               i += 4;
500               j += 4;
501               break;
502           }
503         } else {
504           if (j >= value.GetLength() || format[i] != value[j]) {
505             bBadFormat = true;
506             bExit = true;
507           }
508           i++;
509           j++;
510         }
511 
512         if (oldj == j) {
513           bBadFormat = true;
514           bExit = true;
515         }
516         break;
517       }
518 
519       default:
520         if (value.GetLength() <= j) {
521           bExit = true;
522         } else if (format[i] != value[j]) {
523           bBadFormat = true;
524           bExit = true;
525         }
526 
527         i++;
528         j++;
529         break;
530     }
531   }
532 
533   if (bBadFormat)
534     return ConversionStatus::kBadFormat;
535 
536   if (bPm)
537     nHour += 12;
538 
539   if (nYear >= 0 && nYear <= nYearSub)
540     nYear += 2000;
541 
542   if (!FX_IsValidMonth(nMonth) || !FX_IsValidDay(nDay) ||
543       !FX_IsValid24Hour(nHour) || !FX_IsValidMinute(nMin) ||
544       !FX_IsValidSecond(nSec)) {
545     return ConversionStatus::kBadDate;
546   }
547 
548   dt = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
549                    FX_MakeTime(nHour, nMin, nSec, 0));
550   if (std::isnan(dt))
551     return ConversionStatus::kBadDate;
552 
553   *result = dt;
554   return ConversionStatus::kSuccess;
555 }
556 
557 }  // namespace fxjs
558