• 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                 return false;
147             }
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 {
161             if (!proxy->GetDate().IsStringEnd()) {
162                 return false;
163             }
164         }
165     }
166     if (timeZone->IsLocal() && timeValue->GetIndex() == 0) {
167         timeZone->SetUTC();
168     }
169     dayValue->SetIsoFlag(true);
170     return true;
171 }
172 
ParseLegacyDates(DateProxy * proxy,DayValue * dayValue,TimeValue * timeValue,TimeZone * timeZone)173 bool DateParse::ParseLegacyDates(DateProxy *proxy, DayValue *dayValue, TimeValue *timeValue, TimeZone *timeZone)
174 {
175     DateUnit date = proxy->NextDate();
176     bool hasNumber = (dayValue->GetIndex() > 0);
177     while (!date.IsStringEnd()) {
178         if (date.IsNumber()) {
179             hasNumber = true;
180             int num = date.GetValue();
181             // first parse as time "hh:" or "mm:"
182             if (proxy->GetDate().IsSymbol(':')) {
183                 // skip ':'
184                 proxy->NextDate();
185                 if (!proxy->GetDate().IsNumber()) {
186                     return false;
187                 }
188                 if (!timeValue->SetData(num)) {
189                     return false;
190                 }
191             // second parse as "ss.sss"
192             } else if (proxy->GetDate().IsSymbol('.') && timeValue->IsValidSecond(num)) {
193                 // skip '.'
194                 proxy->NextDate();
195                 timeValue->SetData(num);
196                 DateUnit milliSec = proxy->GetDate();
197                 if (!milliSec.IsNumber()) {
198                     return false;
199                 }
200                 timeValue->SetData(TimeValue::NormMilliSecond(milliSec));
201                 // ship "sss"
202                 proxy->NextDate();
203                 if (!proxy->GetDate().IsValidFinallyTime()) {
204                     return false;
205                 }
206             // then parse time "mm" or "ss"
207             } else if (timeValue->IsValid(num)) {
208                 timeValue->SetData(num);
209                 if (!proxy->GetDate().IsValidFinallyTime()) {
210                     return false;
211                 }
212             } else {
213                 if (!dayValue->SetData(num)) {
214                     return false;
215                 }
216             }
217         } else if (date.IsMonth()) {
218             dayValue->SetMonth(date.GetValue());
219         } else if (date.IsTimeZone() && hasNumber) {
220             timeZone->SetUTC();
221         } else if (date.IsTimeFlag() || date.IsInvalidWord()) {
222             if (hasNumber) {
223                 return false;
224             }
225             if (proxy->GetDate().IsNumber()) {
226                 return false;
227             }
228         } else if (date.IsSign() && ((timeValue->GetIndex() > 0) || timeZone->IsUTC())) {
229             if (date.IsSymbol('-')) {
230                 timeZone->SetSign(-1);
231             } else {
232                 timeZone->SetSign(1);
233             }
234             DateUnit timeNumUnit = proxy->GetDate();
235             if (!timeNumUnit.IsNumber()) {
236                 return false;
237             }
238             int timeNum = timeNumUnit.GetValue();
239             uint32_t numLength = timeNumUnit.GetLength();
240             proxy->NextDate();
241             // parse +hh:mm
242             if (proxy->GetDate().IsSymbol(':')) {
243                 // skip ':'
244                 proxy->NextDate();
245                 if (!proxy->GetDate().IsNumber()) {
246                     return false;
247                 }
248                 timeZone->SetHour(timeNum);
249                 timeZone->SetMin(proxy->GetDate().GetValue());
250                 proxy->NextDate();
251             // 2: hour length
252             } else if (numLength == 1 || numLength == 2) {
253                 // parse GMT+hh
254                 timeZone->SetHour(timeNum);
255                 timeZone->SetMin(0);
256             // 3,4:"GMT+hhmm" hhmm length
257             } else if (numLength == 3 || numLength == 4) {
258                 // parse GMT+hhmm
259                 timeZone->SetHour(timeNum / JSDate::HUNDRED);
260                 timeZone->SetMin(timeNum % JSDate::HUNDRED);
261             } else {
262                 return false;
263             }
264         }
265         date = proxy->NextDate();
266     }
267     return true;
268 }
269 
Read()270 DateParse::DateUnit DateParse::DateProxy::Read()
271 {
272     if (str_->IsDigit()) {
273         int len = 0;
274         int num = str_->ReadNumber(&len);
275         return DateUnit::Number(num, len);
276     }
277     if (str_->IsEnd()) {
278         return DateUnit::StringEnd();
279     }
280     if (str_->IsChar(':')) {
281         str_->NextChar();
282         return DateUnit::Symbol(':');
283     }
284     if (str_->IsChar('+')) {
285         str_->NextChar();
286         return DateUnit::Symbol('+');
287     }
288     if (str_->IsChar('-')) {
289         str_->NextChar();
290         return DateUnit::Symbol('-');
291     }
292     if (str_->IsChar('.')) {
293         str_->NextChar();
294         return DateUnit::Symbol('.');
295     }
296     if (str_->IsAlpha()) {
297         // 3: month name length
298         char buf[3] = {0};
299         int len = str_->ReadAlphabet(buf, 3);
300         int minLen = len < 3 ? len : 3;
301         CString str(buf, minLen);
302         int value = 0;
303         DateValueType type = MatchKeyWord(str, &value);
304         return DateUnit::Word(type, value, len);
305     }
306     if (str_->IsSpaceOrTab()) {
307         str_->NextChar();
308         return DateUnit::Space();
309     }
310     str_->NextChar();
311     return DateUnit::Unknown();
312 }
313 
MatchKeyWord(const CString & str,int * value)314 DateParse::DateValueType DateParse::DateProxy::MatchKeyWord(const CString &str, int *value)
315 {
316     if (str == "t") {
317         return DATE_TIME_FALG;
318     }
319     if (str == "z") {
320         return DATE_TIME_ZONE;
321     }
322 
323     if (str == "utc" || str == "gmt") {
324         *value = 1;
325         return DATE_TIME_ZONE;
326     }
327     std::array<CString, MOUTH_PER_YEAR> monthName = {
328         "jan", "feb", "mar", "apr", "may", "jun",
329         "jul", "aug", "sep", "oct", "nov", "dec"
330         };
331     for (int i = 0; i < MOUTH_PER_YEAR; i++) {
332         if (str == monthName[i]) {
333             *value = i + 1;
334             return DATE_MONTH;
335         }
336     }
337     return DATE_INVALID_WORD;
338 }
339 
SetTimeZone(int * time)340 bool DateParse::TimeZone::SetTimeZone(int *time)
341 {
342     if (!IsLocal()) {
343         if (!TimeValue::HourIsValid(hour_) || !TimeValue::MinuteIsValid(min_)) {
344             return false;
345         }
346         time[TIMEZONE] = sign_ * (hour_ * SEC_PER_HOUR + min_ * SEC_PER_MINUTE);
347     } else {
348         time[TIMEZONE] = INT_MAX;
349     }
350     return true;
351 }
352 
SetTimeValue(int * time)353 bool DateParse::TimeValue::SetTimeValue(int *time)
354 {
355     for (int i = 0; i < TIME_LEN; i++) {
356         if (i < index_) {
357             time[HOUR + i] = data_[i];
358         } else {
359             time[HOUR + i] = 0;
360         }
361     }
362     // 24: allow 24:00:00
363     if (time[HOUR] == 24) {
364         if (time[MIN] == 0 && time[SEC] == 0 && time[MS] == 0) {
365             return true;
366         }
367         return false;
368     }
369     if (!HourIsValid(time[HOUR]) || !MinuteIsValid(time[MIN]) || !SecondIsValid(time[SEC]) ||
370         !MilliSecondIsValid(time[MS])) {
371         return false;
372     }
373     return true;
374 }
375 
SetDayValue(int * time)376 bool DateParse::DayValue::SetDayValue(int *time)
377 {
378     if (index_ == 0) {
379         return false;
380     }
381     int year = 0;
382     int mon = 1;
383     int day = 1;
384     for (int i = index_; i < DAY_LEN; i++) {
385         data_[i] = 1;
386     }
387     if (month_ == INT_MAX) {
388         // 2:index of year
389         if (is_iso_flag_ || (IsFull() && DayIsValid(data_[2]) && !MonthIsValid(data_[0]))) {
390             year = data_[YEAR];
391             mon = data_[MONTH];
392             day = data_[DAYS];
393         } else {
394             if (IsFull()) {
395                 // input:month-day-year
396                 year = data_[2]; // 2:index of year
397                 mon = data_[0];  // 0:index of month
398                 day = data_[1];  // 1:index of day
399             } else {
400                 // input:year-month
401                 year = data_[0]; // 0:index of year
402                 mon = data_[1];  // 1:index of month
403                 day = data_[2];  // 2:index of day
404             }
405         }
406     } else {
407         mon = month_;
408         if (IsFull()) {
409             return false;
410         }
411         // 2: day and year
412         if (index_ == 2) {
413             if (DayIsValid(data_[0]) && !DayIsValid(data_[1])) {
414                 day = data_[0];
415                 year = data_[1];
416             } else {
417                 year = data_[0];
418                 day = data_[1];
419             }
420         } else {
421             day = data_[0];
422         }
423     }
424     if (!MonthIsValid(mon) || !DayIsValid(day)) {
425         return false;
426     }
427     time[YEAR] = year;
428     time[MONTH] = mon - 1;
429     time[DAYS] = day;
430     return true;
431 }
432 }  // namespace panda::ecmascript