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