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