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