• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/date_parse.h"
17 
18 namespace panda::ecmascript {
19     const std::array<CString, MOUTH_PER_YEAR> DateParse::MONTH_NAME = {
20     "jan", "feb", "mar", "apr", "may", "jun",
21     "jul", "aug", "sep", "oct", "nov", "dec"
22 };
ParseDateString(const char * str,int length,int * time)23 bool DateParse::ParseDateString(const char *str, int length, int *time)
24 {
25     StringReader reader(str, length);
26     DateProxy proxy(&reader);
27     DayValue dayValue;
28     TimeValue timeValue;
29     TimeZone timeZone;
30     bool isIso = IsIsoDateTime(&proxy, &dayValue);
31     bool result;
32     if (isIso) {
33         result =  ParseIsoDateTime(&proxy, &dayValue, &timeValue, &timeZone);
34     } else {
35         result = ParseLegacyDates(&proxy, &dayValue, &timeValue, &timeZone);
36     }
37     if (result) {
38         bool success = timeZone.SetTimeZone(time) && timeValue.SetTimeValue(time) && dayValue.SetDayValue(time);
39         return success;
40     } else {
41         return false;
42     }
43 }
44 
IsIsoDateTime(DateProxy * proxy,DayValue * dayValue)45 bool DateParse::IsIsoDateTime(DateProxy *proxy, DayValue *dayValue)
46 {
47     if (proxy->GetDate().IsSign()) {
48         DateUnit sign = proxy->NextDate();
49         if (!proxy->GetDate().IsSixDecimalDigit()) {
50             return false;
51         }
52         int signYear = proxy->NextDate().GetValue();
53         if (sign.IsSymbol('-') && signYear == 0) {
54             return false;
55         }
56         if (sign.IsSymbol('-')) {
57             signYear = -signYear;
58         }
59         dayValue->SetData(signYear);
60     } else if (proxy->GetDate().IsFourDecimalDigit()) {
61         int year = proxy->NextDate().GetValue();
62         dayValue->SetData(year);
63     } else {
64         return false;
65     }
66     if (proxy->GetDate().IsSymbol('-')) {
67         proxy->NextDate();
68         DateUnit mon = proxy->GetDate();
69         if (!mon.IsTwoDecimalDigit()) {
70             return false;
71         }
72         dayValue->SetData(mon.GetValue());
73         proxy->NextDate();
74         if (proxy->GetDate().IsSymbol('-')) {
75             proxy->NextDate();
76             DateUnit day = proxy->GetDate();
77             if (!day.IsTwoDecimalDigit()) {
78                 return false;
79             }
80             dayValue->SetData(day.GetValue());
81             proxy->NextDate();
82         }
83     }
84     if (!proxy->GetDate().IsTimeFlag()) {
85         if (!proxy->GetDate().IsStringEnd()) {
86             return false;
87         }
88     }
89     return true;
90 }
91 
ParseIsoDateTime(DateProxy * proxy,DayValue * dayValue,TimeValue * timeValue,TimeZone * timeZone)92 bool DateParse::ParseIsoDateTime(DateProxy *proxy, DayValue *dayValue, TimeValue *timeValue, TimeZone *timeZone)
93 {
94     if (proxy->GetDate().IsTimeFlag()) {
95         // skip 'T'
96         proxy->NextDate();
97         DateUnit hour = proxy->GetDate();
98         if (!hour.IsTwoDecimalDigit()) {
99             return false;
100         }
101         timeValue->SetData(hour.GetValue());
102         proxy->NextDate();
103         if (!proxy->GetDate().IsSymbol(':')) {
104             return false;
105         }
106         // skip ':'
107         proxy->NextDate();
108         DateUnit min = proxy->GetDate();
109         if (!min.IsTwoDecimalDigit()) {
110             return false;
111         }
112         timeValue->SetData(min.GetValue());
113         proxy->NextDate();
114         if (proxy->GetDate().IsSymbol(':')) {
115             // skip ':'
116             proxy->NextDate();
117             DateUnit second = proxy->GetDate();
118             if (!second.IsTwoDecimalDigit()) {
119                 return false;
120             }
121             timeValue->SetData(second.GetValue());
122             proxy->NextDate();
123             if (proxy->GetDate().IsSymbol('.')) {
124                 // skip '.'
125                 proxy->NextDate();
126                 DateUnit milliSec = proxy->GetDate();
127                 if (!milliSec.IsNumber()) {
128                     return false;
129                 }
130                 timeValue->SetData(TimeValue::NormMilliSecond(milliSec));
131                 proxy->NextDate();
132             }
133         }
134         // parse 'z' | '+' | '-' time zone
135         if (proxy->GetDate().IsWordZ()) {
136             timeZone->SetUTC();
137             proxy->NextDate();
138         } else if (proxy->GetDate().IsSign()) {
139             if (proxy->GetDate().IsSymbol('-')) {
140                 timeZone->SetSign(-1);
141             } else {
142                 timeZone->SetSign(1);
143             }
144             // skip '+' | '-'
145             proxy->NextDate();
146             DateUnit hourZone = proxy->GetDate();
147             if (hourZone.IsTwoDecimalDigit()) {
148                 timeZone->SetHour(hourZone.GetValue());
149                 proxy->NextDate();
150                 if (!proxy->GetDate().IsSymbol(':')) {
151                     return false;
152                 }
153                 proxy->NextDate();
154                 DateUnit minZone = proxy->GetDate();
155                 if (!minZone.IsTwoDecimalDigit()) {
156                     return false;
157                 }
158                 timeZone->SetMin(minZone.GetValue());
159                 proxy->NextDate();
160             } else if (hourZone.IsFourDecimalDigit()) {
161                 timeZone->SetHour(hourZone.GetValue() / JSDate::HUNDRED);
162                 timeZone->SetMin(hourZone.GetValue() % JSDate::HUNDRED);
163                 proxy->NextDate();
164             } else {
165                 return false;
166             }
167         } else {
168             if (!proxy->GetDate().IsStringEnd()) {
169                 return false;
170             }
171         }
172     }
173     if (timeZone->IsLocal() && timeValue->GetIndex() == 0) {
174         timeZone->SetUTC();
175     }
176     dayValue->SetIsoFlag(true);
177     return true;
178 }
179 
ParseLegacyDates(DateProxy * proxy,DayValue * dayValue,TimeValue * timeValue,TimeZone * timeZone)180 bool DateParse::ParseLegacyDates(DateProxy *proxy, DayValue *dayValue, TimeValue *timeValue, TimeZone *timeZone)
181 {
182     DateUnit date = proxy->NextDate();
183     bool hasNumber = (dayValue->GetIndex() > 0);
184     while (!date.IsStringEnd()) {
185         if (date.IsNumber()) {
186             hasNumber = true;
187             int num = date.GetValue();
188             // first parse as time "hh:" or "mm:"
189             if (proxy->GetDate().IsSymbol(':')) {
190                 // skip ':'
191                 proxy->NextDate();
192                 if (!proxy->GetDate().IsNumber()) {
193                     return false;
194                 }
195                 if (!timeValue->SetData(num)) {
196                     return false;
197                 }
198             // second parse as "ss.sss"
199             } else if (proxy->GetDate().IsSymbol('.') && timeValue->IsValidSecond(num)) {
200                 // skip '.'
201                 proxy->NextDate();
202                 timeValue->SetData(num);
203                 DateUnit milliSec = proxy->GetDate();
204                 if (!milliSec.IsNumber()) {
205                     return false;
206                 }
207                 timeValue->SetData(TimeValue::NormMilliSecond(milliSec));
208                 // ship "sss"
209                 proxy->NextDate();
210                 if (!proxy->GetDate().IsValidFinallyTime()) {
211                     return false;
212                 }
213             // then parse time "mm" or "ss"
214             } else if (timeValue->IsValid(num)) {
215                 timeValue->SetData(num);
216                 if (!proxy->GetDate().IsValidFinallyTime()) {
217                     return false;
218                 }
219             } else {
220                 if (!dayValue->SetData(num)) {
221                     return false;
222                 }
223             }
224         } else if (date.IsMonth()) {
225             dayValue->SetMonth(date.GetValue());
226         } else if (date.IsTimeZone() && hasNumber) {
227             timeZone->SetUTC();
228         } else if (date.IsTimeFlag() || date.IsInvalidWord()) {
229             if (hasNumber) {
230                 return false;
231             }
232             if (proxy->GetDate().IsNumber()) {
233                 return false;
234             }
235         } else if (date.IsSign() && ((timeValue->GetIndex() > 0) || timeZone->IsUTC())) {
236             if (date.IsSymbol('-')) {
237                 timeZone->SetSign(-1);
238             } else {
239                 timeZone->SetSign(1);
240             }
241             DateUnit timeNumUnit = proxy->GetDate();
242             if (!timeNumUnit.IsNumber()) {
243                 return false;
244             }
245             int timeNum = timeNumUnit.GetValue();
246             uint32_t numLength = timeNumUnit.GetLength();
247             proxy->NextDate();
248             // parse +hh:mm
249             if (proxy->GetDate().IsSymbol(':')) {
250                 // skip ':'
251                 proxy->NextDate();
252                 if (!proxy->GetDate().IsNumber()) {
253                     return false;
254                 }
255                 timeZone->SetHour(timeNum);
256                 timeZone->SetMin(proxy->GetDate().GetValue());
257                 proxy->NextDate();
258             // 2: hour length
259             } else if (numLength == 1 || numLength == 2) {
260                 // parse GMT+hh
261                 timeZone->SetHour(timeNum);
262                 timeZone->SetMin(0);
263             // 3,4:"GMT+hhmm" hhmm length
264             } else if (numLength == 3 || numLength == 4) {
265                 // parse GMT+hhmm
266                 timeZone->SetHour(timeNum / JSDate::HUNDRED);
267                 timeZone->SetMin(timeNum % JSDate::HUNDRED);
268             } else {
269                 return false;
270             }
271         }
272         date = proxy->NextDate();
273     }
274     return true;
275 }
276 
Read()277 DateParse::DateUnit DateParse::DateProxy::Read()
278 {
279     if (str_->IsDigit()) {
280         int len = 0;
281         int num = str_->ReadNumber(&len);
282         return DateUnit::Number(num, len);
283     }
284     if (str_->IsEnd()) {
285         return DateUnit::StringEnd();
286     }
287     if (str_->IsChar(':')) {
288         str_->NextChar();
289         return DateUnit::Symbol(':');
290     }
291     if (str_->IsChar('+')) {
292         str_->NextChar();
293         return DateUnit::Symbol('+');
294     }
295     if (str_->IsChar('-')) {
296         str_->NextChar();
297         return DateUnit::Symbol('-');
298     }
299     if (str_->IsChar('.')) {
300         str_->NextChar();
301         return DateUnit::Symbol('.');
302     }
303     if (str_->IsAlpha()) {
304         // 3: month name length
305         char buf[3] = {0};
306         int len = str_->ReadAlphabet(buf, 3);
307         int minLen = len < 3 ? len : 3;
308         CString str(buf, minLen);
309         int value = 0;
310         DateValueType type = MatchKeyWord(str, &value);
311         return DateUnit::Word(type, value, len);
312     }
313     if (str_->IsSpaceOrTab()) {
314         str_->NextChar();
315         return DateUnit::Space();
316     }
317     str_->NextChar();
318     return DateUnit::Unknown();
319 }
320 
MatchKeyWord(const CString & str,int * value)321 DateParse::DateValueType DateParse::DateProxy::MatchKeyWord(const CString &str, int *value)
322 {
323     if (str == "t") {
324         return DATE_TIME_FALG;
325     }
326     if (str == "z") {
327         return DATE_TIME_ZONE;
328     }
329 
330     if (str == "utc" || str == "gmt") {
331         *value = 1;
332         return DATE_TIME_ZONE;
333     }
334     for (int i = 0; i < MOUTH_PER_YEAR; i++) {
335         if (str == DateParse::MONTH_NAME[i]) {
336             *value = i + 1;
337             return DATE_MONTH;
338         }
339     }
340     return DATE_INVALID_WORD;
341 }
342 
SetTimeZone(int * time)343 bool DateParse::TimeZone::SetTimeZone(int *time)
344 {
345     if (!IsLocal()) {
346         if (!TimeValue::HourIsValid(hour_) || !TimeValue::MinuteIsValid(min_)) {
347             return false;
348         }
349         time[TIMEZONE] = sign_ * (hour_ * SEC_PER_HOUR + min_ * SEC_PER_MINUTE);
350     } else {
351         time[TIMEZONE] = INT_MAX;
352     }
353     return true;
354 }
355 
SetTimeValue(int * time)356 bool DateParse::TimeValue::SetTimeValue(int *time)
357 {
358     for (int i = 0; i < TIME_LEN; i++) {
359         if (i < index_) {
360             time[HOUR + i] = data_[i];
361         } else {
362             time[HOUR + i] = 0;
363         }
364     }
365     // 24: allow 24:00:00
366     if (time[HOUR] == 24) {
367         if (time[MIN] == 0 && time[SEC] == 0 && time[MS] == 0) {
368             return true;
369         }
370         return false;
371     }
372     if (!HourIsValid(time[HOUR]) || !MinuteIsValid(time[MIN]) || !SecondIsValid(time[SEC]) ||
373         !MilliSecondIsValid(time[MS])) {
374         return false;
375     }
376     return true;
377 }
378 
SetDayValue(int * time)379 bool DateParse::DayValue::SetDayValue(int *time)
380 {
381     if (index_ == 0) {
382         return false;
383     }
384     int year = 1;
385     int mon = 1;
386     int day = 1;
387     for (int i = index_; i < DAY_LEN; i++) {
388         data_[i] = 1;
389     }
390     if (month_ == INT_MAX) {
391         // 2:index of year
392         if (isIsoFlag_ || (IsFull() && DayIsValid(data_[2]) && !MonthIsValid(data_[0]))) {
393             year = data_[YEAR];
394             mon = data_[MONTH];
395             day = data_[DAYS];
396         } else {
397             if (IsFull()) {
398                 // input:month-day-year
399                 year = data_[2]; // 2:index of year
400                 mon = data_[0];  // 0:index of month
401                 day = data_[1];  // 1:index of day
402             } else {
403                 // input:year-month
404                 year = data_[0]; // 0:index of year
405                 mon = data_[1];  // 1:index of month
406                 day = data_[2];  // 2:index of day
407             }
408         }
409     } else {
410         mon = month_;
411         if (IsFull()) {
412             return false;
413         }
414         // 2: day and year
415         if (index_ == 2) {
416             if (!DayIsValid(data_[0])) {
417                 year = data_[0];
418                 day = data_[1];
419             } else {
420                 day = data_[0];
421                 year = data_[1];
422             }
423         } else {
424             day = data_[0];
425         }
426     }
427     if (!IsIso()) {
428         if (IsBetween(year, 0, 49)) { // if year is between 0-49
429             year += 2000; // 2000 : it means 2001-2049
430         } else if (IsBetween(year, 50, 99)) { // if year is between 50-99
431             year += 1900; // 1900 : it may means 1950-1999
432         }
433     }
434     if (!MonthIsValid(mon) || !DayIsValid(day)) {
435         return false;
436     }
437     time[YEAR] = year;
438     time[MONTH] = mon - 1;
439     time[DAYS] = day;
440     return true;
441 }
442 }  // namespace panda::ecmascript