Home | History | Annotate | Download | only in src
      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 
     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 
     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 
     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.
    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 
    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(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