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