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