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#ifndef ECMASCRIPT_DATE_PARSE_H
17#define ECMASCRIPT_DATE_PARSE_H
18
19#include "ecmascript/js_date.h"
20
21namespace panda::ecmascript {
22class DateParse {
23public:
24    static const std::array<CString, MOUTH_PER_YEAR> MONTH_NAME;
25    static bool ParseDateString(const char *str, int length, int *time);
26
27private:
28    static bool IsBetween(int n, int lower, int hign)
29    {
30        if (n < lower || n > hign) {
31            return false;
32        }
33        return true;
34    }
35    class StringReader {
36        public:
37            explicit StringReader(const char *str, int length) : data_(str), length_(length)
38            {
39                NextChar();
40            }
41
42            void NextChar()
43            {
44                value_ = (index_ < length_) ? data_[index_] : DEL;
45                index_++;
46            }
47
48            int GetIndex() const
49            {
50                return index_;
51            }
52
53            int ReadNumber(int *len)
54            {
55                int index = 0;
56                int num = 0;
57                while (IsDigit()) {
58                    num = (value_ - '0') + num * JSDate::TEN;
59                    index++;
60                    const int maxDecimal = (std::numeric_limits<int>::max() - JSDate::NUM_NINE) / JSDate::TEN;
61                    if (num > maxDecimal) {
62                        break;
63                    }
64                    NextChar();
65                }
66                // read the followed digit
67                while (IsDigit()) {
68                    NextChar();
69                }
70                *len = index;
71                return num;
72            }
73
74            int ReadAlphabet(char *word, int size)
75            {
76                int length = 0;
77                for (; IsAlpha(); length++) {
78                    if (length < size) {
79                        word[length] = GetLower(value_);
80                    }
81                    NextChar();
82                }
83                return length;
84            }
85
86            char GetLower(char ch)
87            {
88                if (ch >= 'A' && ch <= 'Z') {
89                    // 32: 'a' - 'A'
90                    return ch + 32;
91                }
92                return ch;
93            }
94
95            bool IsDigit() const
96            {
97                if (value_ >= '0' && value_ <= '9') {
98                    return true;
99                }
100                return false;
101            }
102
103            bool IsSign() const
104            {
105                if (value_ == '+' || value_ == '-') {
106                    return true;
107                }
108                return false;
109            }
110
111            bool IsEnd() const
112            {
113                return value_ == DEL;
114            }
115
116            bool IsThisChar(char ch) const
117            {
118                return value_ == ch;
119            }
120
121            bool IsAlpha() const
122            {
123                if (value_ >= 'A' && value_ <= 'z') {
124                    return true;
125                }
126                return false;
127            }
128
129            bool IsSpaceOrTab() const
130            {
131                if (value_ == ' ' || value_ == '\t') {
132                    return true;
133                }
134                return false;
135            }
136
137            bool IsChar(char ch)
138            {
139                if (value_ != ch) {
140                    return false;
141                }
142                return true;
143            }
144        private:
145            const char *data_;
146            int index_ {0};
147            int length_;
148            char value_;
149    };
150
151    enum DateValueType : int8_t {
152        DATE_INVALID,
153        DATE_UNKNOWN,
154        DATE_NUMBER,
155        DATE_SYMBOL,
156        DATE_SPACE,
157        DATE_STRING_END,
158        DATE_TIME_ZONE,
159        DATE_TIME_FALG,
160        DATE_MONTH,
161        DATE_INVALID_WORD,
162        DATE_WORD_START = DATE_TIME_ZONE,
163    };
164
165    class DateUnit {
166        public:
167            bool IsInvalid() const
168            {
169                return type_ == DATE_INVALID;
170            }
171
172            bool IsUnknown() const
173            {
174                return type_ == DATE_UNKNOWN;
175            }
176
177            bool IsNumber() const
178            {
179                return type_ == DATE_NUMBER;
180            }
181
182            bool IsSymbol() const
183            {
184                return type_ == DATE_SYMBOL;
185            }
186
187            bool IsSymbol(char ch) const
188            {
189                return type_ == DATE_SYMBOL && static_cast<int>(ch) == value_;
190            }
191
192            bool IsStringEnd() const
193            {
194                return type_ == DATE_STRING_END;
195            }
196
197            bool IsTimeZone() const
198            {
199                return type_ == DATE_TIME_ZONE;
200            }
201
202            bool IsTimeFlag() const
203            {
204                return type_ == DATE_TIME_FALG;
205            }
206
207            bool IsInvalidWord() const
208            {
209                return type_ == DATE_INVALID_WORD;
210            }
211
212            bool IsMonth() const
213            {
214                return type_ == DATE_MONTH;
215            }
216
217            bool IsWord() const
218            {
219                return type_ >= DATE_TIME_ZONE;
220            }
221
222            bool IsSign() const
223            {
224                return type_ == DATE_SYMBOL && (value_ == '-' || value_ == '+');
225            }
226
227            bool IsSixDecimalDigit() const
228            {
229                // 6: 6 decimal digit
230                return type_ == DATE_NUMBER && len_ == 6;
231            }
232
233            bool IsFourDecimalDigit() const
234            {
235                // 4: 4 decimal digit
236                return type_ == DATE_NUMBER && len_ == 4;
237            }
238
239            bool IsTwoDecimalDigit() const
240            {
241                // 2: 2 decimal digit
242                return type_ == DATE_NUMBER && len_ == 2;
243            }
244
245            bool IsWordZ() const
246            {
247                return type_ == DATE_TIME_ZONE && value_ == 0;
248            }
249
250            bool IsSpaceOrTab() const
251            {
252                return type_ == DATE_SPACE;
253            }
254
255            bool IsValidFinallyTime()
256            {
257                return IsStringEnd() || IsSign() || IsWordZ() || IsSpaceOrTab();
258            }
259
260            static DateUnit Number(int value, int len)
261            {
262                return DateUnit(DATE_NUMBER, value, len);
263            }
264
265            static DateUnit Symbol(char ch)
266            {
267                return DateUnit(DATE_SYMBOL, static_cast<int>(ch), 1);
268            }
269
270            static DateUnit Word(DateValueType type, int value, int len)
271            {
272                return DateUnit(type, value, len);
273            }
274
275            static DateUnit Space()
276            {
277                return DateUnit(DATE_SPACE, 0, 1);
278            }
279
280            static DateUnit StringEnd()
281            {
282                return DateUnit(DATE_STRING_END, 0, 0);
283            }
284
285            static DateUnit Invalid()
286            {
287                return DateUnit(DATE_INVALID, 0, 0);
288            }
289
290            static DateUnit Unknown()
291            {
292                return DateUnit(DATE_UNKNOWN, 0, 1);
293            }
294
295            int GetValue() const
296            {
297                return value_;
298            }
299
300            char GetSymbol() const
301            {
302                return static_cast<char>(value_);
303            }
304
305            DateValueType GetType() const
306            {
307                return type_;
308            }
309
310            uint32_t GetLength() const
311            {
312                return len_;
313            }
314        private:
315            explicit DateUnit(DateValueType type, int value, int len) : type_(type), value_(value), len_(len) {}
316            DateValueType type_;
317            int value_;
318            uint32_t len_;
319    };
320
321    class DateProxy {
322        public:
323            explicit DateProxy(StringReader *str) : str_(str), date_(Read()) {}
324            DateUnit GetDate() const
325            {
326                return date_;
327            }
328
329            DateUnit NextDate()
330            {
331                DateUnit cur = GetDate();
332                date_ = Read();
333                return cur;
334            }
335
336            bool IgnoreSymbol(char ch)
337            {
338                if (!date_.IsSymbol(ch)) {
339                    return false;
340                }
341                date_ = Read();
342                return true;
343            }
344        private:
345            DateUnit Read();
346            DateValueType MatchKeyWord(const CString &str, int *value);
347
348            StringReader *str_;
349            DateUnit date_;
350    };
351
352    class TimeZone {
353        public:
354            void SetSign(int sign)
355            {
356                sign_ = sign;
357            }
358
359            void SetHour(int hour)
360            {
361                hour_ = hour;
362            }
363
364            void SetMin(int min)
365            {
366                min_ = min;
367            }
368
369            void SetUTC()
370            {
371                sign_ = 1;
372                hour_ = 0;
373                min_ = 0;
374            }
375
376            bool IsUTC()
377            {
378                return (hour_ == 0 && min_ == 0);
379            }
380
381            bool IsLocal() const
382            {
383                return hour_ == INT_MAX;
384            }
385
386            bool SetTimeZone(int *time);
387        private:
388            int sign_ {INT_MAX};
389            int hour_ {INT_MAX};
390            int min_ {INT_MAX};
391    };
392
393    class TimeValue {
394        public:
395            bool SetData(int data)
396            {
397                if (index_ < TIME_LEN) {
398                    data_[index_] = data;
399                    index_++;
400                    return true;
401                }
402                return false;
403            }
404
405            static bool MinuteIsValid(int n)
406            {
407                // 59 : max min
408                return IsBetween(n, 0, 59);
409            }
410
411            static bool SecondIsValid(int n)
412            {
413                // 59 : max sec
414                return IsBetween(n, 0, 59);
415            }
416
417            static bool HourIsValid(int n)
418            {
419                // 24: max hour
420                return IsBetween(n, 0, 24);
421            }
422
423            static bool MilliSecondIsValid(int n)
424            {
425                // 999 : max millisecond
426                return IsBetween(n, 0, 999);
427            }
428
429            static int NormMilliSecond(DateUnit sec)
430            {
431                uint32_t len = sec.GetLength();
432                int value = sec.GetValue();
433                // 3: "sss" norm length
434                if (len == 3) {
435                    return value;
436                }
437                // 2: ms length
438                if (len == 2) {
439                    return value * JSDate::TEN;
440                }
441                if (len == 1) {
442                    return value * JSDate::HUNDRED;
443                }
444                int divisor = 1;
445                // 3: "sss" norm length
446                while (len > 3) {
447                    divisor *= JSDate::TEN;
448                    len--;
449                }
450                return value / divisor;
451            }
452
453            int GetIndex() const
454            {
455                return index_;
456            }
457
458            bool IsValid(int n) const
459            {
460                // 2: index of second
461                return (index_ == 1 && MinuteIsValid(n)) || (index_ == 2 && SecondIsValid(n));
462            }
463
464            bool IsValidSecond(int n) const
465            {
466                // 2: index of second
467                return (index_ == 2 && SecondIsValid(n));
468            }
469
470            bool SetTimeValue(int *time);
471        private:
472            static constexpr int TIME_LEN = 4;
473            int data_[TIME_LEN];
474            int index_ {0};
475    };
476
477    class DayValue {
478        public:
479            bool SetData(int data)
480            {
481                if (index_ < DAY_LEN) {
482                    data_[index_++] = data;
483                    return true;
484                }
485                return false;
486            }
487
488            void SetIsoFlag(bool flag)
489            {
490                isIsoFlag_ = flag;
491            }
492
493            void SetMonth(int month)
494            {
495                month_ = month;
496            }
497
498            static bool MonthIsValid(int n)
499            {
500                return IsBetween(n, 1, MOUTH_PER_YEAR);
501            }
502
503            static bool DayIsValid(int n)
504            {
505                return IsBetween(n, 1, JSDate::MAX_DAYS_MONTH);
506            }
507
508            bool IsIso() const
509            {
510                return isIsoFlag_;
511            }
512
513            bool IsFull() const
514            {
515                return index_ == DAY_LEN;
516            }
517
518            int GetIndex() const
519            {
520                return index_;
521            }
522
523            bool SetDayValue(int *time);
524        private:
525            static constexpr int DAY_LEN = 3;
526            int data_[DAY_LEN];
527            int index_ {0};
528            int month_ {INT_MAX};
529            bool isIsoFlag_ {false};
530    };
531    static bool IsIsoDateTime(DateProxy *proxy, DayValue *dayValue);
532    static bool ParseIsoDateTime(DateProxy *proxy, DayValue *dayValue, TimeValue *timeValue,
533        TimeZone *timeZone);
534    static bool ParseLegacyDates(DateProxy *proxy, DayValue *dayValue, TimeValue *timeValue,
535        TimeZone *timeZone);
536};
537}  // namespace panda::ecmascript
538
539#endif  // ECMASCRIPT_DATE_PARSE_H
540