// -------------------------------------------------------------------
// Reused output buffer. Used when parsing date strings.
-var parse_buffer = $Array(7);
+var parse_buffer = $Array(8);
// ECMA 262 - 15.9.4.2
function DateParse(string) {
if (IS_NULL(arr)) return $NaN;
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);
- if (IS_NULL(arr[6])) {
+ if (IS_NULL(arr[7])) {
return TimeClip(UTC(date));
} else {
- return TimeClip(date - arr[6] * 1000);
+ return TimeClip(date - arr[7] * 1000);
}
}
} else {
// n + ":"
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)) {
tz.SetAbsoluteMinute(n);
} else if (time.IsExpecting(n)) {
time.AddFinal(n);
- // Require end or white space immediately after finalizing time.
- if (!in.IsEnd() && !in.SkipWhiteSpace()) return false;
+ // Require end, white space or Z immediately after finalizing time.
+ if (!in.IsEnd() && !in.SkipWhiteSpace() && !in.Is('Z')) return false;
} else {
if (!day.Add(n)) return false;
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()) {
// Parse a "word" (sequence of chars. >= 'A').
namespace internal {
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 month = kNone;
int day = kNone;
int& hour = comp_[0];
int& minute = comp_[1];
int& second = comp_[2];
+ int& millisecond = comp_[3];
if (hour_offset_ != kNone) {
if (!IsHour12(hour)) return false;
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(MINUTE, Smi::FromInt(minute));
output->set(SECOND, Smi::FromInt(second));
+ output->set(MILLISECOND, Smi::FromInt(millisecond));
return true;
}
{'p', 'm', '\0', DateParser::AM_PM, 12},
{'u', 't', '\0', 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},
{'c', 'd', 't', DateParser::TIME_ZONE_NAME, -5},
{'c', 's', 't', DateParser::TIME_ZONE_NAME, -6},
// [3]: hour
// [4]: minute
// [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).
template <typename Char>
static bool Parse(Vector<Char> str, FixedArray* output);
enum {
- YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, UTC_OFFSET, OUTPUT_SIZE
+ YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, UTC_OFFSET, OUTPUT_SIZE
};
private:
TimeComposer() : index_(0), hour_offset_(kNone) {}
bool IsEmpty() const { return index_ == 0; }
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) {
return index_ < kSize ? (comp_[index_++] = n, true) : false;
static bool IsHour(int x) { return Between(x, 0, 23); }
static bool IsHour12(int x) { return Between(x, 0, 12); }
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 index_;
int hour_offset_;
'Saturday, 01-Jan-00 01:00:00 PDT',
'01 Jan 00 01:00 -0700'];
-
// Local time cases.
var testCasesLocalTime = [
// Allow timezone ommision.
['Saturday, 01-Jan-00 08:00 PM UT', 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.
testCasesUT.forEach(testDateParse);
testCasesLocalTime.forEach(testDateParseLocalTime);
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.
// (Dates from 1970 to ~2070 with 150h steps.)