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