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