From: Lars Knoll Date: Mon, 21 Jan 2013 10:39:08 +0000 (+0100) Subject: Fix Date constructor and add Date.prototype.toISOString X-Git-Tag: upstream/5.2.1~669^2~659^2~462 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9b5112258b06f87be919f4ac4b7d72fee83cb0d2;p=platform%2Fupstream%2Fqtdeclarative.git Fix Date constructor and add Date.prototype.toISOString Change-Id: Ib905639ed903cccdeb2649374f744a1fc64f3c1c Reviewed-by: Simon Hausmann --- diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c3bd81c..df3ff9b 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -366,6 +366,144 @@ static inline double FromDateTime(const QDateTime &dt) static inline double ParseString(const QString &s) { + // first try the format defined in 15.9.1.15, only if that fails fall back to + // QDateTime for parsing + + // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ + // It can be date or time only, and the second and later components + // of both fields are optional + // and extended syntax for negative and large positive years exists: +/-YYYYYY + + enum Format { + Year, + Month, + Day, + Hour, + Minute, + Second, + MilliSecond, + TimezoneHour, + TimezoneMinute, + Done + }; + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + uint format = Year; + int current = 0; + int currentSize = 0; + bool extendedYear = false; + + int yearSign = 1; + int year = 0; + int month = 0; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + int msec = 0; + int offsetSign = 1; + int offset = 0; + + bool error = false; + if (*ch == '+' || *ch == '-') { + extendedYear = true; + if (*ch == '-') + yearSign = -1; + ++ch; + } + while (ch <= end) { + if (*ch >= '0' && *ch <= '9') { + current *= 10; + current += ch->unicode() - '0'; + ++currentSize; + } else { // other char, delimits field + switch (format) { + case Year: + year = current; + if (extendedYear) + error = (currentSize != 6); + else + error = (currentSize != 4); + break; + case Month: + month = current - 1; + error = (currentSize != 2) || month > 11; + break; + case Day: + day = current; + error = (currentSize != 2) || day > 31; + break; + case Hour: + hour = current; + error = (currentSize != 2) || hour > 24; + break; + case Minute: + minute = current; + error = (currentSize != 2) || minute > 60; + break; + case Second: + second = current; + error = (currentSize != 2) || second > 60; + break; + case MilliSecond: + msec = current; + error = (currentSize != 3); + break; + case TimezoneHour: + offset = current*60; + error = (currentSize != 2) || offset > 23*60; + break; + case TimezoneMinute: + offset += current; + error = (currentSize != 2) || current >= 60; + break; + } + if (*ch == 'T') { + if (format >= Hour) + error = true; + format = Hour; + } else if (*ch == '-') { + if (format < Day) + ++format; + else if (format < Minute) + error = true; + else if (format >= TimezoneHour) + error = true; + else { + offsetSign = -1; + format = TimezoneHour; + } + } else if (*ch == ':') { + if (format != Hour && format != Minute && format != TimezoneHour) + error = true; + ++format; + } else if (*ch == '.') { + if (format != Second) + error = true; + ++format; + } else if (*ch == '+') { + if (format < Minute || format >= TimezoneHour) + error = true; + format = TimezoneHour; + } else if (*ch == 'Z' || *ch == 0) { + format = Done; + } + current = 0; + currentSize = 0; + } + if (error || format == Done) + break; + ++ch; + } + + if (!error) { + double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); + t += offset * offsetSign * 60 * 1000; + return t; + } + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::ISODate); @@ -2433,7 +2571,7 @@ Value DateCtor::construct(ExecutionContext *ctx) arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT); if (arg.isString()) - t = ParseString(arg.toString(ctx)->toQString()); + t = ParseString(arg.stringValue()->toQString()); else t = TimeClip(arg.toNumber(ctx)); } @@ -2516,6 +2654,7 @@ void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); } double DatePrototype::getThisDate(ExecutionContext *ctx) @@ -2528,24 +2667,6 @@ double DatePrototype::getThisDate(ExecutionContext *ctx) } } -Value DatePrototype::method_MakeTime(ExecutionContext *ctx) -{ - ctx->throwUnimplemented(QStringLiteral("Data.MakeTime")); - return Value::undefinedValue(); -} - -Value DatePrototype::method_MakeDate(ExecutionContext *ctx) -{ - ctx->throwUnimplemented(QStringLiteral("Data.MakeDate")); - return Value::undefinedValue(); -} - -Value DatePrototype::method_TimeClip(ExecutionContext *ctx) -{ - ctx->throwUnimplemented(QStringLiteral("Data.TimeClip")); - return Value::undefinedValue(); -} - Value DatePrototype::method_parse(ExecutionContext *ctx) { return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); @@ -3007,6 +3128,57 @@ Value DatePrototype::method_toUTCString(ExecutionContext *ctx) return Value::fromString(ctx, ToUTCString(t)); } +static void addZeroPrefixedInt(QString &str, int num, int nDigits) +{ + str.resize(str.size() + nDigits); + + QChar *c = str.data() + str.size() - 1; + while (nDigits) { + *c = QChar(num % 10 + '0'); + num /= 10; + --c; + --nDigits; + } +} + +Value DatePrototype::method_toISOString(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (!std::isfinite(t)) + ctx->throwRangeError(ctx->thisObject); + + QString result; + int year = (int)YearFromTime(t); + if (year < 0 || year > 9999) { + if (qAbs(year) >= 1000000) + return Value::fromString(ctx, QStringLiteral("Invalid Date")); + result += year < 0 ? '-' : '+'; + year = qAbs(year); + addZeroPrefixedInt(result, year, 6); + } else { + addZeroPrefixedInt(result, year, 4); + } + result += '-'; + addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); + result += '-'; + addZeroPrefixedInt(result, (int)DateFromTime(t), 2); + result += 'T'; + addZeroPrefixedInt(result, HourFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, MinFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, SecFromTime(t), 2); + result += '.'; + addZeroPrefixedInt(result, msFromTime(t), 3); + result += 'Z'; + + return Value::fromString(ctx, result); +} + // // RegExp object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index ffaf803..571dc17 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -235,11 +235,9 @@ struct DatePrototype: DateObject static double getThisDate(ExecutionContext *ctx); - static Value method_MakeTime(ExecutionContext *ctx); - static Value method_MakeDate(ExecutionContext *ctx); - static Value method_TimeClip(ExecutionContext *ctx); static Value method_parse(ExecutionContext *ctx); static Value method_UTC(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); static Value method_toDateString(ExecutionContext *ctx); static Value method_toTimeString(ExecutionContext *ctx); @@ -283,6 +281,7 @@ struct DatePrototype: DateObject static Value method_setFullYear(ExecutionContext *ctx); static Value method_setUTCFullYear(ExecutionContext *ctx); static Value method_toUTCString(ExecutionContext *ctx); + static Value method_toISOString(ExecutionContext *ctx); }; struct RegExpCtor: FunctionObject diff --git a/tests/TestExpectations b/tests/TestExpectations index 6b3a232..39c0a49 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -524,7 +524,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-11 failing 15.2.3.3-4-12 failing 15.2.3.3-4-13 failing -15.2.3.3-4-161 failing 15.2.3.3-4-162 failing 15.2.3.3-4-164 failing 15.2.3.3-4-51 failing @@ -547,7 +546,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-360-7 failing 15.2.3.6-4-621 failing 15.2.3.6-4-622 failing -15.2.3.6-4-623 failing 15.2.3.6-4-624 failing 15.2.3.7-6-a-110 failing 15.2.3.7-6-a-280 failing @@ -960,7 +958,6 @@ S15.7.4.5_A1.4_T01 failing 15.9.4.4-0-2 failing 15.9.4.4-0-3 failing 15.9.4.4-0-4 failing -15.9.1.15-1 failing S15.9.2.1_A2 failing S15.9.3.1_A5_T1 failing S15.9.3.1_A5_T2 failing @@ -1090,18 +1087,6 @@ S15.9.5.1_A2_T1 failing 13.3.0_2 failing 13.3.0_6 failing 13.3.0_7 failing -15.9.5.43-0-10 failing -15.9.5.43-0-11 failing -15.9.5.43-0-12 failing -15.9.5.43-0-13 failing -15.9.5.43-0-14 failing -15.9.5.43-0-15 failing -15.9.5.43-0-2 failing -15.9.5.43-0-3 failing -15.9.5.43-0-4 failing -15.9.5.43-0-5 failing -15.9.5.43-0-8 failing -15.9.5.43-0-9 failing 15.9.5.44-0-1 failing 15.9.5.44-0-2 failing 6.2.2_a failing