blob: 15f762ddc839ae6f1186b831e073bcffee1417ec [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 Zhang78572ea2019-02-26 19:44:59 +0000168 size_t i = nStart;
169 while (i < str.GetLength() && std::iswalnum(str[i]))
170 ++i;
Lei Zhang4d246232019-02-26 22:29:39 +0000171 return i - nStart;
Lei Zhang78572ea2019-02-26 19:44:59 +0000172}
173
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000174} // namespace
175
Tom Sepezf71b5632018-11-07 23:41:52 +0000176const wchar_t* const kMonths[12] = {L"Jan", L"Feb", L"Mar", L"Apr",
177 L"May", L"Jun", L"Jul", L"Aug",
178 L"Sep", L"Oct", L"Nov", L"Dec"};
179
180const wchar_t* const kFullMonths[12] = {L"January", L"February", L"March",
181 L"April", L"May", L"June",
182 L"July", L"August", L"September",
183 L"October", L"November", L"December"};
184
Lei Zhang4d246232019-02-26 22:29:39 +0000185static constexpr size_t KMonthAbbreviationLength = 3; // Anything in |kMonths|.
186static constexpr size_t kLongestFullMonthLength = 9; // September
187
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000188double FX_GetDateTime() {
189 if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
190 return 0;
191
192 time_t t = FXSYS_time(nullptr);
193 struct tm* pTm = FXSYS_localtime(&t);
194 double t1 = TimeFromYear(pTm->tm_year + 1900);
195 return t1 + pTm->tm_yday * 86400000.0 + pTm->tm_hour * 3600000.0 +
196 pTm->tm_min * 60000.0 + pTm->tm_sec * 1000.0;
197}
198
199int FX_GetYearFromTime(double dt) {
200 return YearFromTime(dt);
201}
202
203int FX_GetMonthFromTime(double dt) {
204 return MonthFromTime(dt);
205}
206
207int FX_GetDayFromTime(double dt) {
208 return DateFromTime(dt);
209}
210
211int FX_GetHourFromTime(double dt) {
212 return (int)Mod(floor(dt / (60 * 60 * 1000)), 24);
213}
214
215int FX_GetMinFromTime(double dt) {
216 return (int)Mod(floor(dt / (60 * 1000)), 60);
217}
218
219int FX_GetSecFromTime(double dt) {
220 return (int)Mod(floor(dt / 1000), 60);
221}
222
Tom Sepezf71b5632018-11-07 23:41:52 +0000223bool FX_IsValidMonth(int m) {
224 return m >= 1 && m <= 12;
225}
226
227// TODO(thestig): Should this take the month into consideration?
228bool FX_IsValidDay(int d) {
229 return d >= 1 && d <= 31;
230}
231
232// TODO(thestig): Should 24 be allowed? Similarly, 60 for minutes and seconds.
233bool FX_IsValid24Hour(int h) {
234 return h >= 0 && h <= 24;
235}
236
237bool FX_IsValidMinute(int m) {
238 return m >= 0 && m <= 60;
239}
240
241bool FX_IsValidSecond(int s) {
242 return s >= 0 && s <= 60;
243}
244
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000245double FX_LocalTime(double d) {
246 return d + GetLocalTZA() + GetDaylightSavingTA(d);
247}
248
249double FX_MakeDay(int nYear, int nMonth, int nDate) {
250 double y = static_cast<double>(nYear);
251 double m = static_cast<double>(nMonth);
252 double dt = static_cast<double>(nDate);
253 double ym = y + floor(m / 12);
254 double mn = Mod(m, 12);
255 double t = TimeFromYearMonth(static_cast<int>(ym), static_cast<int>(mn));
256 if (YearFromTime(t) != ym || MonthFromTime(t) != mn || DateFromTime(t) != 1)
257 return std::nan("");
258
259 return Day(t) + dt - 1;
260}
261
262double FX_MakeTime(int nHour, int nMin, int nSec, int nMs) {
263 double h = static_cast<double>(nHour);
264 double m = static_cast<double>(nMin);
265 double s = static_cast<double>(nSec);
266 double milli = static_cast<double>(nMs);
267 return h * 3600000 + m * 60000 + s * 1000 + milli;
268}
269
270double FX_MakeDate(double day, double time) {
271 if (!std::isfinite(day) || !std::isfinite(time))
272 return std::nan("");
273
274 return day * 86400000 + time;
275}
276
Tom Sepezd105d2e2018-11-02 16:56:37 +0000277int FX_ParseStringInteger(const WideString& str,
278 size_t nStart,
279 size_t* pSkip,
280 size_t nMaxStep) {
281 int nRet = 0;
282 size_t nSkip = 0;
283 for (size_t i = nStart; i < str.GetLength(); ++i) {
284 if (i - nStart > 10)
285 break;
286
287 wchar_t c = str[i];
Lei Zhang4609c5d2018-12-07 20:10:54 +0000288 if (!FXSYS_IsDecimalDigit(c))
Tom Sepezd105d2e2018-11-02 16:56:37 +0000289 break;
290
291 nRet = nRet * 10 + FXSYS_DecimalCharToInt(c);
292 ++nSkip;
293 if (nSkip >= nMaxStep)
294 break;
295 }
296
297 *pSkip = nSkip;
298 return nRet;
299}
300
Tom Sepezf71b5632018-11-07 23:41:52 +0000301ConversionStatus FX_ParseDateUsingFormat(const WideString& value,
302 const WideString& format,
303 double* result) {
304 double dt = FX_GetDateTime();
305 if (format.IsEmpty() || value.IsEmpty()) {
306 *result = dt;
307 return ConversionStatus::kSuccess;
308 }
309
310 int nYear = FX_GetYearFromTime(dt);
311 int nMonth = FX_GetMonthFromTime(dt) + 1;
312 int nDay = FX_GetDayFromTime(dt);
313 int nHour = FX_GetHourFromTime(dt);
314 int nMin = FX_GetMinFromTime(dt);
315 int nSec = FX_GetSecFromTime(dt);
316 int nYearSub = 99; // nYear - 2000;
317 bool bPm = false;
318 bool bExit = false;
319 bool bBadFormat = false;
320 size_t i = 0;
321 size_t j = 0;
322
323 while (i < format.GetLength()) {
324 if (bExit)
325 break;
326
327 wchar_t c = format[i];
328 switch (c) {
329 case ':':
330 case '.':
331 case '-':
332 case '\\':
333 case '/':
334 i++;
335 j++;
336 break;
337
338 case 'y':
339 case 'm':
340 case 'd':
341 case 'H':
342 case 'h':
343 case 'M':
344 case 's':
345 case 't': {
346 size_t oldj = j;
347 size_t nSkip = 0;
348 size_t remaining = format.GetLength() - i - 1;
349
350 if (remaining == 0 || format[i + 1] != c) {
351 switch (c) {
352 case 'y':
353 i++;
354 j++;
355 break;
356 case 'm':
357 nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
358 i++;
359 j += nSkip;
360 break;
361 case 'd':
362 nDay = 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 'h':
372 nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
373 i++;
374 j += nSkip;
375 break;
376 case 'M':
377 nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
378 i++;
379 j += nSkip;
380 break;
381 case 's':
382 nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
383 i++;
384 j += nSkip;
385 break;
386 case 't':
387 bPm = (j < value.GetLength() && value[j] == 'p');
388 i++;
389 j++;
390 break;
391 }
392 } else if (remaining == 1 || format[i + 2] != c) {
393 switch (c) {
394 case 'y':
395 nYear = FX_ParseStringInteger(value, j, &nSkip, 2);
396 i += 2;
397 j += nSkip;
398 break;
399 case 'm':
400 nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
401 i += 2;
402 j += nSkip;
403 break;
404 case 'd':
405 nDay = 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 'h':
415 nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
416 i += 2;
417 j += nSkip;
418 break;
419 case 'M':
420 nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
421 i += 2;
422 j += nSkip;
423 break;
424 case 's':
425 nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
426 i += 2;
427 j += nSkip;
428 break;
429 case 't':
430 bPm = (j + 1 < value.GetLength() && value[j] == 'p' &&
431 value[j + 1] == 'm');
432 i += 2;
433 j += 2;
434 break;
435 }
436 } else if (remaining == 2 || format[i + 3] != c) {
437 switch (c) {
438 case 'm': {
Tom Sepezf71b5632018-11-07 23:41:52 +0000439 bool bFind = false;
Lei Zhang4d246232019-02-26 22:29:39 +0000440 nSkip = FindSubWordLength(value, j);
441 if (nSkip == KMonthAbbreviationLength) {
442 WideString sMonth = value.Mid(j, KMonthAbbreviationLength);
443 for (size_t m = 0; m < FX_ArraySize(kMonths); ++m) {
444 if (sMonth.CompareNoCase(kMonths[m]) == 0) {
445 nMonth = m + 1;
446 i += 3;
447 j += nSkip;
448 bFind = true;
449 break;
450 }
Tom Sepezf71b5632018-11-07 23:41:52 +0000451 }
452 }
453
454 if (!bFind) {
455 nMonth = FX_ParseStringInteger(value, j, &nSkip, 3);
456 i += 3;
457 j += nSkip;
458 }
459 } break;
460 case 'y':
461 break;
462 default:
463 i += 3;
464 j += 3;
465 break;
466 }
467 } else if (remaining == 3 || format[i + 4] != c) {
468 switch (c) {
469 case 'y':
470 nYear = FX_ParseStringInteger(value, j, &nSkip, 4);
471 j += nSkip;
472 i += 4;
473 break;
474 case 'm': {
475 bool bFind = false;
Lei Zhang4d246232019-02-26 22:29:39 +0000476 nSkip = FindSubWordLength(value, j);
477 if (nSkip <= kLongestFullMonthLength) {
478 WideString sMonth = value.Mid(j, nSkip);
479 sMonth.MakeLower();
480 for (size_t m = 0; m < FX_ArraySize(kFullMonths); ++m) {
481 WideString sFullMonths = WideString(kFullMonths[m]);
482 sFullMonths.MakeLower();
483 if (sFullMonths.Contains(sMonth.c_str())) {
484 nMonth = m + 1;
485 i += 4;
486 j += nSkip;
487 bFind = true;
488 break;
489 }
Tom Sepezf71b5632018-11-07 23:41:52 +0000490 }
491 }
492
493 if (!bFind) {
494 nMonth = FX_ParseStringInteger(value, j, &nSkip, 4);
495 i += 4;
496 j += nSkip;
497 }
498 } break;
499 default:
500 i += 4;
501 j += 4;
502 break;
503 }
504 } else {
505 if (j >= value.GetLength() || format[i] != value[j]) {
506 bBadFormat = true;
507 bExit = true;
508 }
509 i++;
510 j++;
511 }
512
513 if (oldj == j) {
514 bBadFormat = true;
515 bExit = true;
516 }
517 break;
518 }
519
520 default:
521 if (value.GetLength() <= j) {
522 bExit = true;
523 } else if (format[i] != value[j]) {
524 bBadFormat = true;
525 bExit = true;
526 }
527
528 i++;
529 j++;
530 break;
531 }
532 }
533
534 if (bBadFormat)
535 return ConversionStatus::kBadFormat;
536
537 if (bPm)
538 nHour += 12;
539
540 if (nYear >= 0 && nYear <= nYearSub)
541 nYear += 2000;
542
543 if (!FX_IsValidMonth(nMonth) || !FX_IsValidDay(nDay) ||
544 !FX_IsValid24Hour(nHour) || !FX_IsValidMinute(nMin) ||
545 !FX_IsValidSecond(nSec)) {
546 return ConversionStatus::kBadDate;
547 }
548
549 dt = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
550 FX_MakeTime(nHour, nMin, nSec, 0));
551 if (std::isnan(dt))
552 return ConversionStatus::kBadDate;
553
554 *result = dt;
555 return ConversionStatus::kSuccess;
556}
557
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000558} // namespace fxjs