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