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