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 10namespace v8 { 11namespace internal { 12 13bool 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 67bool 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 99bool 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 119const 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. 152int 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 169int 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