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