• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/date/dateparser.h"
6 
7 #include "src/objects/objects-inl.h"
8 #include "src/strings/char-predicates-inl.h"
9 
10 namespace v8 {
11 namespace internal {
12 
Write(double * output)13 bool DateParser::DayComposer::Write(double* output) {
14   if (index_ < 1) return false;
15   // Day and month defaults to 1.
16   while (index_ < kSize) {
17     comp_[index_++] = 1;
18   }
19 
20   int year = 0;  // Default year is 0 (=> 2000) for KJS compatibility.
21   int month = kNone;
22   int day = kNone;
23 
24   if (named_month_ == kNone) {
25     if (is_iso_date_ || (index_ == 3 && !IsDay(comp_[0]))) {
26       // YMD
27       year = comp_[0];
28       month = comp_[1];
29       day = comp_[2];
30     } else {
31       // MD(Y)
32       month = comp_[0];
33       day = comp_[1];
34       if (index_ == 3) year = comp_[2];
35     }
36   } else {
37     month = named_month_;
38     if (index_ == 1) {
39       // MD or DM
40       day = comp_[0];
41     } else if (!IsDay(comp_[0])) {
42       // YMD, MYD, or YDM
43       year = comp_[0];
44       day = comp_[1];
45     } else {
46       // DMY, MDY, or DYM
47       day = comp_[0];
48       year = comp_[1];
49     }
50   }
51 
52   if (!is_iso_date_) {
53     if (Between(year, 0, 49))
54       year += 2000;
55     else if (Between(year, 50, 99))
56       year += 1900;
57   }
58 
59   if (!Smi::IsValid(year) || !IsMonth(month) || !IsDay(day)) return false;
60 
61   output[YEAR] = year;
62   output[MONTH] = month - 1;  // 0-based
63   output[DAY] = day;
64   return true;
65 }
66 
Write(double * output)67 bool DateParser::TimeComposer::Write(double* output) {
68   // All time slots default to 0
69   while (index_ < kSize) {
70     comp_[index_++] = 0;
71   }
72 
73   int& hour = comp_[0];
74   int& minute = comp_[1];
75   int& second = comp_[2];
76   int& millisecond = comp_[3];
77 
78   if (hour_offset_ != kNone) {
79     if (!IsHour12(hour)) return false;
80     hour %= 12;
81     hour += hour_offset_;
82   }
83 
84   if (!IsHour(hour) || !IsMinute(minute) || !IsSecond(second) ||
85       !IsMillisecond(millisecond)) {
86     // A 24th hour is allowed if minutes, seconds, and milliseconds are 0
87     if (hour != 24 || minute != 0 || second != 0 || millisecond != 0) {
88       return false;
89     }
90   }
91 
92   output[HOUR] = hour;
93   output[MINUTE] = minute;
94   output[SECOND] = second;
95   output[MILLISECOND] = millisecond;
96   return true;
97 }
98 
Write(double * output)99 bool DateParser::TimeZoneComposer::Write(double* output) {
100   if (sign_ != kNone) {
101     if (hour_ == kNone) hour_ = 0;
102     if (minute_ == kNone) minute_ = 0;
103     // Avoid signed integer overflow (undefined behavior) by doing unsigned
104     // arithmetic.
105     unsigned total_seconds_unsigned = hour_ * 3600U + minute_ * 60U;
106     if (total_seconds_unsigned > Smi::kMaxValue) return false;
107     int total_seconds = static_cast<int>(total_seconds_unsigned);
108     if (sign_ < 0) {
109       total_seconds = -total_seconds;
110     }
111     DCHECK(Smi::IsValid(total_seconds));
112     output[UTC_OFFSET] = total_seconds;
113   } else {
114     output[UTC_OFFSET] = std::numeric_limits<double>::quiet_NaN();
115   }
116   return true;
117 }
118 
119 const int8_t
120     DateParser::KeywordTable::array[][DateParser::KeywordTable::kEntrySize] = {
121         {'j', 'a', 'n', DateParser::MONTH_NAME, 1},
122         {'f', 'e', 'b', DateParser::MONTH_NAME, 2},
123         {'m', 'a', 'r', DateParser::MONTH_NAME, 3},
124         {'a', 'p', 'r', DateParser::MONTH_NAME, 4},
125         {'m', 'a', 'y', DateParser::MONTH_NAME, 5},
126         {'j', 'u', 'n', DateParser::MONTH_NAME, 6},
127         {'j', 'u', 'l', DateParser::MONTH_NAME, 7},
128         {'a', 'u', 'g', DateParser::MONTH_NAME, 8},
129         {'s', 'e', 'p', DateParser::MONTH_NAME, 9},
130         {'o', 'c', 't', DateParser::MONTH_NAME, 10},
131         {'n', 'o', 'v', DateParser::MONTH_NAME, 11},
132         {'d', 'e', 'c', DateParser::MONTH_NAME, 12},
133         {'a', 'm', '\0', DateParser::AM_PM, 0},
134         {'p', 'm', '\0', DateParser::AM_PM, 12},
135         {'u', 't', '\0', DateParser::TIME_ZONE_NAME, 0},
136         {'u', 't', 'c', DateParser::TIME_ZONE_NAME, 0},
137         {'z', '\0', '\0', DateParser::TIME_ZONE_NAME, 0},
138         {'g', 'm', 't', DateParser::TIME_ZONE_NAME, 0},
139         {'c', 'd', 't', DateParser::TIME_ZONE_NAME, -5},
140         {'c', 's', 't', DateParser::TIME_ZONE_NAME, -6},
141         {'e', 'd', 't', DateParser::TIME_ZONE_NAME, -4},
142         {'e', 's', 't', DateParser::TIME_ZONE_NAME, -5},
143         {'m', 'd', 't', DateParser::TIME_ZONE_NAME, -6},
144         {'m', 's', 't', DateParser::TIME_ZONE_NAME, -7},
145         {'p', 'd', 't', DateParser::TIME_ZONE_NAME, -7},
146         {'p', 's', 't', DateParser::TIME_ZONE_NAME, -8},
147         {'t', '\0', '\0', DateParser::TIME_SEPARATOR, 0},
148         {'\0', '\0', '\0', DateParser::INVALID, 0},
149 };
150 
151 // We could use perfect hashing here, but this is not a bottleneck.
Lookup(const uint32_t * pre,int len)152 int DateParser::KeywordTable::Lookup(const uint32_t* pre, int len) {
153   int i;
154   for (i = 0; array[i][kTypeOffset] != INVALID; i++) {
155     int j = 0;
156     while (j < kPrefixLength && pre[j] == static_cast<uint32_t>(array[i][j])) {
157       j++;
158     }
159     // Check if we have a match and the length is legal.
160     // Word longer than keyword is only allowed for month names.
161     if (j == kPrefixLength &&
162         (len <= kPrefixLength || array[i][kTypeOffset] == MONTH_NAME)) {
163       return i;
164     }
165   }
166   return i;
167 }
168 
ReadMilliseconds(DateToken token)169 int DateParser::ReadMilliseconds(DateToken token) {
170   // Read first three significant digits of the original numeral,
171   // as inferred from the value and the number of digits.
172   // I.e., use the number of digits to see if there were
173   // leading zeros.
174   int number = token.number();
175   int length = token.length();
176   if (length < 3) {
177     // Less than three digits. Multiply to put most significant digit
178     // in hundreds position.
179     if (length == 1) {
180       number *= 100;
181     } else if (length == 2) {
182       number *= 10;
183     }
184   } else if (length > 3) {
185     if (length > kMaxSignificantDigits) length = kMaxSignificantDigits;
186     // More than three digits. Divide by 10^(length - 3) to get three
187     // most significant digits.
188     int factor = 1;
189     do {
190       DCHECK_LE(factor, 100000000);  // factor won't overflow.
191       factor *= 10;
192       length--;
193     } while (length > 3);
194     number /= factor;
195   }
196   return number;
197 }
198 
199 }  // namespace internal
200 }  // namespace v8
201