• 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 {
ParseDateString(const char * str,int length,int * time)19 bool DateParse::ParseDateString(const char *str, int length, int *time)
20 {
21     StringReader reader(str, length);
22     DateProxy proxy(&reader);
23     DayValue dayValue;
24     TimeValue timeValue;
25     TimeZone timeZone;
26     bool isIso = IsIsoDateTime(&proxy, &dayValue);
27     bool result;
28     if (isIso) {
29         result =  ParseIsoDateTime(&proxy, &dayValue, &timeValue, &timeZone);
30     } else {
31         result = ParseLegacyDates(&proxy, &dayValue, &timeValue, &timeZone);
32     }
33     if (result) {
34         bool success = timeZone.SetTimeZone(time) && timeValue.SetTimeValue(time) && dayValue.SetDayValue(time);
35         return success;
36     } else {
37         return false;
38     }
39 }
40 
IsIsoDateTime(DateProxy * proxy,DayValue * dayValue)41 bool DateParse::IsIsoDateTime(DateProxy *proxy, DayValue *dayValue)
42 {
43     if (proxy->GetDate().IsSign()) {
44         DateUnit sign = proxy->NextDate();
45         if (!proxy->GetDate().IsSixDecimalDigit()) {
46             return false;
47         }
48         int signYear = proxy->NextDate().GetValue();
49         if (sign.IsSymbol('-') && signYear == 0) {
50             return false;
51         }
52         if (sign.IsSymbol('-')) {
53             signYear = -signYear;
54         }
55         dayValue->SetData(signYear);
56     } else if (proxy->GetDate().IsFourDecimalDigit()) {
57         int year = proxy->NextDate().GetValue();
58         dayValue->SetData(year);
59     } else {
60         return false;
61     }
62     if (proxy->GetDate().IsSymbol('-')) {
63         proxy->NextDate();
64         DateUnit mon = proxy->GetDate();
65         if (!mon.IsTwoDecimalDigit()) {
66             return false;
67         }
68         dayValue->SetData(mon.GetValue());
69         proxy->NextDate();
70         if (proxy->GetDate().IsSymbol('-')) {
71             proxy->NextDate();
72             DateUnit day = proxy->GetDate();
73             if (!day.IsTwoDecimalDigit()) {
74                 return false;
75             }
76             dayValue->SetData(day.GetValue());
77             proxy->NextDate();
78         }
79     }
80     if (!proxy->GetDate().IsTimeFlag()) {
81         if (!proxy->GetDate().IsStringEnd()) {
82             return false;
83         }
84     }
85     return true;
86 }
87 
ParseIsoDateTime(DateProxy * proxy,DayValue * dayValue,TimeValue * timeValue,TimeZone * timeZone)88 bool DateParse::ParseIsoDateTime(DateProxy *proxy, DayValue *dayValue, TimeValue *timeValue, TimeZone *timeZone)
89 {
90     if (proxy->GetDate().IsTimeFlag()) {
91         // skip 'T'
92         proxy->NextDate();
93         DateUnit hour = proxy->GetDate();
94         if (!hour.IsTwoDecimalDigit()) {
95             return false;
96         }
97         timeValue->SetData(hour.GetValue());
98         proxy->NextDate();
99         if (!proxy->GetDate().IsSymbol(':')) {
100             return false;
101         }
102         // skip ':'
103         proxy->NextDate();
104         DateUnit min = proxy->GetDate();
105         if (!min.IsTwoDecimalDigit()) {
106             return false;
107         }
108         timeValue->SetData(min.GetValue());
109         proxy->NextDate();
110         if (proxy->GetDate().IsSymbol(':')) {
111             // skip ':'
112             proxy->NextDate();
113             DateUnit second = proxy->GetDate();
114             if (!second.IsTwoDecimalDigit()) {
115                 return false;
116             }
117             timeValue->SetData(second.GetValue());
118             proxy->NextDate();
119             if (proxy->GetDate().IsSymbol('.')) {
120                 // skip '.'
121                 proxy->NextDate();
122                 DateUnit milliSec = proxy->GetDate();
123                 if (!milliSec.IsNumber()) {
124                     return false;
125                 }
126                 timeValue->SetData(TimeValue::NormMilliSecond(milliSec));
127                 proxy->NextDate();
128             }
129         }
130         // parse 'z' | '+' | '-' time zone
131         if (proxy->GetDate().IsWordZ()) {
132             timeZone->SetUTC();
133             proxy->NextDate();
134         } else if (proxy->GetDate().IsSign()) {
135             if (proxy->GetDate().IsSymbol('-')) {
136                 timeZone->SetSign(-1);
137             } else {
138                 timeZone->SetSign(1);
139             }
140             // skip '+' | '-'
141             proxy->NextDate();
142             DateUnit hourZone = proxy->GetDate();
143             if (hourZone.IsTwoDecimalDigit()) {
144                 timeZone->SetHour(hourZone.GetValue());
145                 proxy->NextDate();
146                 if (!proxy->GetDate().IsSymbol(':')) {
147                     return false;
148                 }
149                 proxy->NextDate();
150                 DateUnit minZone = proxy->GetDate();
151                 if (!minZone.IsTwoDecimalDigit()) {
152                     return false;
153                 }
154                 timeZone->SetMin(minZone.GetValue());
155                 proxy->NextDate();
156             } else if (hourZone.IsFourDecimalDigit()) {
157                 timeZone->SetHour(hourZone.GetValue() / JSDate::HUNDRED);
158                 timeZone->SetMin(hourZone.GetValue() % JSDate::HUNDRED);
159                 proxy->NextDate();
160             } else {
161                 return false;
162             }
163         } else {
164             if (!proxy->GetDate().IsStringEnd()) {
165                 return false;
166             }
167         }
168     }
169     if (timeZone->IsLocal() && timeValue->GetIndex() == 0) {
170         timeZone->SetUTC();
171     }
172     dayValue->SetIsoFlag(true);
173     return true;
174 }
175 
ParseLegacyDates(DateProxy * proxy,DayValue * dayValue,TimeValue * timeValue,TimeZone * timeZone)176 bool DateParse::ParseLegacyDates(DateProxy *proxy, DayValue *dayValue, TimeValue *timeValue, TimeZone *timeZone)
177 {
178     DateUnit date = proxy->NextDate();
179     bool hasNumber = (dayValue->GetIndex() > 0);
180     while (!date.IsStringEnd()) {
181         if (date.IsNumber()) {
182             hasNumber = true;
183             int num = date.GetValue();
184             // first parse as time "hh:" or "mm:"
185             if (proxy->GetDate().IsSymbol(':')) {
186                 // skip ':'
187                 proxy->NextDate();
188                 if (!proxy->GetDate().IsNumber()) {
189                     return false;
190                 }
191                 if (!timeValue->SetData(num)) {
192                     return false;
193                 }
194             // second parse as "ss.sss"
195             } else if (proxy->GetDate().IsSymbol('.') && timeValue->IsValidSecond(num)) {
196                 // skip '.'
197                 proxy->NextDate();
198                 timeValue->SetData(num);
199                 DateUnit milliSec = proxy->GetDate();
200                 if (!milliSec.IsNumber()) {
201                     return false;
202                 }
203                 timeValue->SetData(TimeValue::NormMilliSecond(milliSec));
204                 // ship "sss"
205                 proxy->NextDate();
206                 if (!proxy->GetDate().IsValidFinallyTime()) {
207                     return false;
208                 }
209             // then parse time "mm" or "ss"
210             } else if (timeValue->IsValid(num)) {
211                 timeValue->SetData(num);
212                 if (!proxy->GetDate().IsValidFinallyTime()) {
213                     return false;
214                 }
215             } else {
216                 if (!dayValue->SetData(num)) {
217                     return false;
218                 }
219             }
220         } else if (date.IsMonth()) {
221             dayValue->SetMonth(date.GetValue());
222         } else if (date.IsTimeZone() && hasNumber) {
223             timeZone->SetUTC();
224         } else if (date.IsTimeFlag() || date.IsInvalidWord()) {
225             if (hasNumber) {
226                 return false;
227             }
228             if (proxy->GetDate().IsNumber()) {
229                 return false;
230             }
231         } else if (date.IsSign() && ((timeValue->GetIndex() > 0) || timeZone->IsUTC())) {
232             if (date.IsSymbol('-')) {
233                 timeZone->SetSign(-1);
234             } else {
235                 timeZone->SetSign(1);
236             }
237             DateUnit timeNumUnit = proxy->GetDate();
238             if (!timeNumUnit.IsNumber()) {
239                 return false;
240             }
241             int timeNum = timeNumUnit.GetValue();
242             uint32_t numLength = timeNumUnit.GetLength();
243             proxy->NextDate();
244             // parse +hh:mm
245             if (proxy->GetDate().IsSymbol(':')) {
246                 // skip ':'
247                 proxy->NextDate();
248                 if (!proxy->GetDate().IsNumber()) {
249                     return false;
250                 }
251                 timeZone->SetHour(timeNum);
252                 timeZone->SetMin(proxy->GetDate().GetValue());
253                 proxy->NextDate();
254             // 2: hour length
255             } else if (numLength == 1 || numLength == 2) {
256                 // parse GMT+hh
257                 timeZone->SetHour(timeNum);
258                 timeZone->SetMin(0);
259             // 3,4:"GMT+hhmm" hhmm length
260             } else if (numLength == 3 || numLength == 4) {
261                 // parse GMT+hhmm
262                 timeZone->SetHour(timeNum / JSDate::HUNDRED);
263                 timeZone->SetMin(timeNum % JSDate::HUNDRED);
264             } else {
265                 return false;
266             }
267         }
268         date = proxy->NextDate();
269     }
270     return true;
271 }
272 
Read()273 DateParse::DateUnit DateParse::DateProxy::Read()
274 {
275     if (str_->IsDigit()) {
276         int len = 0;
277         int num = str_->ReadNumber(&len);
278         return DateUnit::Number(num, len);
279     }
280     if (str_->IsEnd()) {
281         return DateUnit::StringEnd();
282     }
283     if (str_->IsChar(':')) {
284         str_->NextChar();
285         return DateUnit::Symbol(':');
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_->IsAlpha()) {
300         // 3: month name length
301         char buf[3] = {0};
302         int len = str_->ReadAlphabet(buf, 3);
303         int minLen = len < 3 ? len : 3;
304         CString str(buf, minLen);
305         int value = 0;
306         DateValueType type = MatchKeyWord(str, &value);
307         return DateUnit::Word(type, value, len);
308     }
309     if (str_->IsSpaceOrTab()) {
310         str_->NextChar();
311         return DateUnit::Space();
312     }
313     str_->NextChar();
314     return DateUnit::Unknown();
315 }
316 
MatchKeyWord(const CString & str,int * value)317 DateParse::DateValueType DateParse::DateProxy::MatchKeyWord(const CString &str, int *value)
318 {
319     if (str == "t") {
320         return DATE_TIME_FALG;
321     }
322     if (str == "z") {
323         return DATE_TIME_ZONE;
324     }
325 
326     if (str == "utc" || str == "gmt") {
327         *value = 1;
328         return DATE_TIME_ZONE;
329     }
330     std::array<CString, MOUTH_PER_YEAR> monthName = {
331         "jan", "feb", "mar", "apr", "may", "jun",
332         "jul", "aug", "sep", "oct", "nov", "dec"
333         };
334     for (int i = 0; i < MOUTH_PER_YEAR; i++) {
335         if (str == monthName[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