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