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