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 10namespace v8 { 11namespace internal { 12 13bool 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 66bool 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 99bool 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 119const 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. 153int 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 172int 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(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