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