Commit 6ceb02e6 authored by ricow@chromium.org's avatar ricow@chromium.org

Added support for ES5 date time string format to Date.parse.

Review URL: http://codereview.chromium.org/1704016

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4557 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8e56358e
...@@ -620,7 +620,7 @@ function DatePrintString(time) { ...@@ -620,7 +620,7 @@ function DatePrintString(time) {
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Reused output buffer. Used when parsing date strings. // Reused output buffer. Used when parsing date strings.
var parse_buffer = $Array(7); var parse_buffer = $Array(8);
// ECMA 262 - 15.9.4.2 // ECMA 262 - 15.9.4.2
function DateParse(string) { function DateParse(string) {
...@@ -628,13 +628,13 @@ function DateParse(string) { ...@@ -628,13 +628,13 @@ function DateParse(string) {
if (IS_NULL(arr)) return $NaN; if (IS_NULL(arr)) return $NaN;
var day = MakeDay(arr[0], arr[1], arr[2]); var day = MakeDay(arr[0], arr[1], arr[2]);
var time = MakeTime(arr[3], arr[4], arr[5], 0); var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
var date = MakeDate(day, time); var date = MakeDate(day, time);
if (IS_NULL(arr[6])) { if (IS_NULL(arr[7])) {
return TimeClip(UTC(date)); return TimeClip(UTC(date));
} else { } else {
return TimeClip(date - arr[6] * 1000); return TimeClip(date - arr[7] * 1000);
} }
} }
......
...@@ -54,16 +54,25 @@ bool DateParser::Parse(Vector<Char> str, FixedArray* out) { ...@@ -54,16 +54,25 @@ bool DateParser::Parse(Vector<Char> str, FixedArray* out) {
} else { } else {
// n + ":" // n + ":"
if (!time.Add(n)) return false; if (!time.Add(n)) return false;
in.Skip('.');
} }
} else if (in.Skip('.') && time.IsExpecting(n)) {
time.Add(n);
if (!in.IsAsciiDigit()) return false;
int n = in.ReadUnsignedNumber();
time.AddFinal(n);
} else if (tz.IsExpecting(n)) { } else if (tz.IsExpecting(n)) {
tz.SetAbsoluteMinute(n); tz.SetAbsoluteMinute(n);
} else if (time.IsExpecting(n)) { } else if (time.IsExpecting(n)) {
time.AddFinal(n); time.AddFinal(n);
// Require end or white space immediately after finalizing time. // Require end, white space or Z immediately after finalizing time.
if (!in.IsEnd() && !in.SkipWhiteSpace()) return false; if (!in.IsEnd() && !in.SkipWhiteSpace() && !in.Is('Z')) return false;
} else { } else {
if (!day.Add(n)) return false; if (!day.Add(n)) return false;
in.Skip('-'); // Ignore suffix '-' for year, month, or day. in.Skip('-'); // Ignore suffix '-' for year, month, or day.
// Skip trailing 'T' for ECMAScript 5 date string format but make
// sure that it is followed by a digit (for the time).
if (in.Skip('T') && !in.IsAsciiDigit()) return false;
} }
} else if (in.IsAsciiAlphaOrAbove()) { } else if (in.IsAsciiAlphaOrAbove()) {
// Parse a "word" (sequence of chars. >= 'A'). // Parse a "word" (sequence of chars. >= 'A').
......
...@@ -33,6 +33,16 @@ namespace v8 { ...@@ -33,6 +33,16 @@ namespace v8 {
namespace internal { namespace internal {
bool DateParser::DayComposer::Write(FixedArray* output) { bool DateParser::DayComposer::Write(FixedArray* output) {
// Set year to 0 by default.
if (index_ < 1) {
comp_[index_++] = 1;
}
// Day and month defaults to 1.
while (index_ < kSize) {
comp_[index_++] = 1;
}
int year = 0; // Default year is 0 (=> 2000) for KJS compatibility. int year = 0; // Default year is 0 (=> 2000) for KJS compatibility.
int month = kNone; int month = kNone;
int day = kNone; int day = kNone;
...@@ -88,6 +98,7 @@ bool DateParser::TimeComposer::Write(FixedArray* output) { ...@@ -88,6 +98,7 @@ bool DateParser::TimeComposer::Write(FixedArray* output) {
int& hour = comp_[0]; int& hour = comp_[0];
int& minute = comp_[1]; int& minute = comp_[1];
int& second = comp_[2]; int& second = comp_[2];
int& millisecond = comp_[3];
if (hour_offset_ != kNone) { if (hour_offset_ != kNone) {
if (!IsHour12(hour)) return false; if (!IsHour12(hour)) return false;
...@@ -95,11 +106,13 @@ bool DateParser::TimeComposer::Write(FixedArray* output) { ...@@ -95,11 +106,13 @@ bool DateParser::TimeComposer::Write(FixedArray* output) {
hour += hour_offset_; hour += hour_offset_;
} }
if (!IsHour(hour) || !IsMinute(minute) || !IsSecond(second)) return false; if (!IsHour(hour) || !IsMinute(minute) ||
!IsSecond(second) || !IsMillisecond(millisecond)) return false;
output->set(HOUR, Smi::FromInt(hour)); output->set(HOUR, Smi::FromInt(hour));
output->set(MINUTE, Smi::FromInt(minute)); output->set(MINUTE, Smi::FromInt(minute));
output->set(SECOND, Smi::FromInt(second)); output->set(SECOND, Smi::FromInt(second));
output->set(MILLISECOND, Smi::FromInt(millisecond));
return true; return true;
} }
...@@ -134,6 +147,7 @@ const int8_t DateParser::KeywordTable:: ...@@ -134,6 +147,7 @@ const int8_t DateParser::KeywordTable::
{'p', 'm', '\0', DateParser::AM_PM, 12}, {'p', 'm', '\0', DateParser::AM_PM, 12},
{'u', 't', '\0', DateParser::TIME_ZONE_NAME, 0}, {'u', 't', '\0', DateParser::TIME_ZONE_NAME, 0},
{'u', 't', 'c', DateParser::TIME_ZONE_NAME, 0}, {'u', 't', 'c', DateParser::TIME_ZONE_NAME, 0},
{'z', '\0', '\0', DateParser::TIME_ZONE_NAME, 0},
{'g', 'm', 't', DateParser::TIME_ZONE_NAME, 0}, {'g', 'm', 't', DateParser::TIME_ZONE_NAME, 0},
{'c', 'd', 't', DateParser::TIME_ZONE_NAME, -5}, {'c', 'd', 't', DateParser::TIME_ZONE_NAME, -5},
{'c', 's', 't', DateParser::TIME_ZONE_NAME, -6}, {'c', 's', 't', DateParser::TIME_ZONE_NAME, -6},
......
...@@ -44,13 +44,14 @@ class DateParser : public AllStatic { ...@@ -44,13 +44,14 @@ class DateParser : public AllStatic {
// [3]: hour // [3]: hour
// [4]: minute // [4]: minute
// [5]: second // [5]: second
// [6]: UTC offset in seconds, or null value if no timezone specified // [6]: millisecond
// [7]: UTC offset in seconds, or null value if no timezone specified
// If parsing fails, return false (content of output array is not defined). // If parsing fails, return false (content of output array is not defined).
template <typename Char> template <typename Char>
static bool Parse(Vector<Char> str, FixedArray* output); static bool Parse(Vector<Char> str, FixedArray* output);
enum { enum {
YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, UTC_OFFSET, OUTPUT_SIZE YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, UTC_OFFSET, OUTPUT_SIZE
}; };
private: private:
...@@ -189,7 +190,9 @@ class DateParser : public AllStatic { ...@@ -189,7 +190,9 @@ class DateParser : public AllStatic {
TimeComposer() : index_(0), hour_offset_(kNone) {} TimeComposer() : index_(0), hour_offset_(kNone) {}
bool IsEmpty() const { return index_ == 0; } bool IsEmpty() const { return index_ == 0; }
bool IsExpecting(int n) const { bool IsExpecting(int n) const {
return (index_ == 1 && IsMinute(n)) || (index_ == 2 && IsSecond(n)); return (index_ == 1 && IsMinute(n)) ||
(index_ == 2 && IsSecond(n)) ||
(index_ == 3 && IsMillisecond(n));
} }
bool Add(int n) { bool Add(int n) {
return index_ < kSize ? (comp_[index_++] = n, true) : false; return index_ < kSize ? (comp_[index_++] = n, true) : false;
...@@ -207,8 +210,9 @@ class DateParser : public AllStatic { ...@@ -207,8 +210,9 @@ class DateParser : public AllStatic {
static bool IsHour(int x) { return Between(x, 0, 23); } static bool IsHour(int x) { return Between(x, 0, 23); }
static bool IsHour12(int x) { return Between(x, 0, 12); } static bool IsHour12(int x) { return Between(x, 0, 12); }
static bool IsSecond(int x) { return Between(x, 0, 59); } static bool IsSecond(int x) { return Between(x, 0, 59); }
static bool IsMillisecond(int x) { return Between(x, 0, 999); }
static const int kSize = 3; static const int kSize = 4;
int comp_[kSize]; int comp_[kSize];
int index_; int index_;
int hour_offset_; int hour_offset_;
......
...@@ -205,7 +205,6 @@ var testCasesPDT = [ ...@@ -205,7 +205,6 @@ var testCasesPDT = [
'Saturday, 01-Jan-00 01:00:00 PDT', 'Saturday, 01-Jan-00 01:00:00 PDT',
'01 Jan 00 01:00 -0700']; '01 Jan 00 01:00 -0700'];
// Local time cases. // Local time cases.
var testCasesLocalTime = [ var testCasesLocalTime = [
// Allow timezone ommision. // Allow timezone ommision.
...@@ -233,6 +232,27 @@ var testCasesMisc = [ ...@@ -233,6 +232,27 @@ var testCasesMisc = [
['Saturday, 01-Jan-00 08:00 PM UT', 946756800000], ['Saturday, 01-Jan-00 08:00 PM UT', 946756800000],
['01 Jan 00 08:00 PM +0000', 946756800000]]; ['01 Jan 00 08:00 PM +0000', 946756800000]];
// Test different version of the ES5 date time string format.
var testCasesES5Misc = [
['2000-01-01T08:00:00.000Z', 946713600000],
['2000-01-01T08:00:00Z', 946713600000],
['2000-01-01T08:00Z', 946713600000],
['2000-01T08:00:00.000Z', 946713600000],
['2000T08:00:00.000Z', 946713600000],
['2000T08:00Z', 946713600000],
['2000-01T00:00:00.000-08:00', 946713600000],
['2000-01T08:00:00.001Z', 946713600001],
['2000-01T08:00:00.099Z', 946713600099],
['2000-01T08:00:00.999Z', 946713600999],
['2000-01T00:00:00.001-08:00', 946713600001]];
var testCasesES5MiscNegative = [
'2000-01-01TZ',
'2000-01-01T60Z',
'2000-01-01T60:60Z',
'2000-01-0108:00Z',
'2000-01-01T08Z'];
// Run all the tests. // Run all the tests.
testCasesUT.forEach(testDateParse); testCasesUT.forEach(testDateParse);
...@@ -248,6 +268,12 @@ testCasesPDT.forEach(testDateParse); ...@@ -248,6 +268,12 @@ testCasesPDT.forEach(testDateParse);
testCasesLocalTime.forEach(testDateParseLocalTime); testCasesLocalTime.forEach(testDateParseLocalTime);
testCasesMisc.forEach(testDateParseMisc); testCasesMisc.forEach(testDateParseMisc);
// ES5 date time string format compliance.
testCasesES5Misc.forEach(testDateParseMisc);
testCasesES5MiscNegative.forEach(function (s) {
assertTrue(isNaN(Date.parse(s)), s + " is not NaN.");
});
// Test that we can parse our own date format. // Test that we can parse our own date format.
// (Dates from 1970 to ~2070 with 150h steps.) // (Dates from 1970 to ~2070 with 150h steps.)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment