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