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