• 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/dateparser.h"
6 
7 #include "src/char-predicates-inl.h"
8 #include "src/objects-inl.h"
9 
10 namespace v8 {
11 namespace internal {
12 
Write(FixedArray * output)13 bool DateParser::DayComposer::Write(FixedArray* 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)) year += 2000;
54     else if (Between(year, 50, 99)) year += 1900;
55   }
56 
57   if (!Smi::IsValid(year) || !IsMonth(month) || !IsDay(day)) return false;
58 
59   output->set(YEAR, Smi::FromInt(year));
60   output->set(MONTH, Smi::FromInt(month - 1));  // 0-based
61   output->set(DAY, Smi::FromInt(day));
62   return true;
63 }
64 
65 
Write(FixedArray * output)66 bool DateParser::TimeComposer::Write(FixedArray* output) {
67   // All time slots default to 0
68   while (index_ < kSize) {
69     comp_[index_++] = 0;
70   }
71 
72   int& hour = comp_[0];
73   int& minute = comp_[1];
74   int& second = comp_[2];
75   int& millisecond = comp_[3];
76 
77   if (hour_offset_ != kNone) {
78     if (!IsHour12(hour)) return false;
79     hour %= 12;
80     hour += hour_offset_;
81   }
82 
83   if (!IsHour(hour) || !IsMinute(minute) ||
84       !IsSecond(second) || !IsMillisecond(millisecond)) {
85     // A 24th hour is allowed if minutes, seconds, and milliseconds are 0
86     if (hour != 24 || minute != 0 || second != 0 || millisecond != 0) {
87       return false;
88     }
89   }
90 
91   output->set(HOUR, Smi::FromInt(hour));
92   output->set(MINUTE, Smi::FromInt(minute));
93   output->set(SECOND, Smi::FromInt(second));
94   output->set(MILLISECOND, Smi::FromInt(millisecond));
95   return true;
96 }
97 
98 
Write(FixedArray * output)99 bool DateParser::TimeZoneComposer::Write(FixedArray* 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->set(UTC_OFFSET, Smi::FromInt(total_seconds));
113   } else {
114     output->set_null(UTC_OFFSET);
115   }
116   return true;
117 }
118 
119 const int8_t DateParser::KeywordTable::
120     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 
152 // We could use perfect hashing here, but this is not a bottleneck.
Lookup(const uint32_t * pre,int len)153 int DateParser::KeywordTable::Lookup(const uint32_t* pre, int len) {
154   int i;
155   for (i = 0; array[i][kTypeOffset] != INVALID; i++) {
156     int j = 0;
157     while (j < kPrefixLength &&
158            pre[j] == static_cast<uint32_t>(array[i][j])) {
159       j++;
160     }
161     // Check if we have a match and the length is legal.
162     // Word longer than keyword is only allowed for month names.
163     if (j == kPrefixLength &&
164         (len <= kPrefixLength || array[i][kTypeOffset] == MONTH_NAME)) {
165       return i;
166     }
167   }
168   return i;
169 }
170 
171 
ReadMilliseconds(DateToken token)172 int DateParser::ReadMilliseconds(DateToken token) {
173   // Read first three significant digits of the original numeral,
174   // as inferred from the value and the number of digits.
175   // I.e., use the number of digits to see if there were
176   // leading zeros.
177   int number = token.number();
178   int length = token.length();
179   if (length < 3) {
180     // Less than three digits. Multiply to put most significant digit
181     // in hundreds position.
182     if (length == 1) {
183       number *= 100;
184     } else if (length == 2) {
185       number *= 10;
186     }
187   } else if (length > 3) {
188     if (length > kMaxSignificantDigits) length = kMaxSignificantDigits;
189     // More than three digits. Divide by 10^(length - 3) to get three
190     // most significant digits.
191     int factor = 1;
192     do {
193       DCHECK_LE(factor, 100000000);  // factor won't overflow.
194       factor *= 10;
195       length--;
196     } while (length > 3);
197     number /= factor;
198   }
199   return number;
200 }
201 
202 
203 }  // namespace internal
204 }  // namespace v8
205