Bug 430930 - Extend Date.parse to cover ISO 8601 formats, r=graydon.
authorBruce Hoult <bhoult@mozilla.com>
Wed, 26 Aug 2009 18:13:48 -0700
changeset 32576 31242b0c35d8
parent 32575 429fe0f0a0e0
child 32577 da05b5944ddf
push id9160
push usermrbkap@mozilla.com
push date2009-09-16 23:16 +0000
treeherdermozilla-central@5ac5a4d5563e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgraydon
bugs430930
milestone1.9.3a1pre
Bug 430930 - Extend Date.parse to cover ISO 8601 formats, r=graydon.
js/src/jsdate.cpp
js/tests/ecma_5/Date/15.9.4.2.js
js/tests/ecma_5/Date/browser.js
js/tests/ecma_5/Date/shell.js
js/tests/ecma_5/README
js/tests/ecma_5/browser.js
js/tests/ecma_5/shell.js
js/tests/ecma_5/template.js
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -213,22 +213,30 @@ YearFromTime(jsdouble t)
 #define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)
 
 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
 
 /*
  * The following array contains the day of year for the first day of
  * each month, where index 0 is January, and day 0 is January 1.
  */
-static jsdouble firstDayOfMonth[2][12] = {
-    {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
-    {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
+static jsdouble firstDayOfMonth[2][13] = {
+    {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0},
+    {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0}
 };
 
-#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
+#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
+
+static intN
+DaysInMonth(jsint year, jsint month)
+{
+    JSBool leap = (DaysInYear(year) == 366);
+    intN result = DayFromMonth(month, leap) - DayFromMonth(month-1, leap);
+    return result;
+}
 
 static intN
 MonthFromTime(jsdouble t)
 {
     intN d, step;
     jsint year = YearFromTime(t);
     d = DayWithinYear(t, year);
 
@@ -621,16 +629,268 @@ date_UTC(JSContext *cx, uintN argc, jsva
     if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
         return JS_FALSE;
 
     msec_time = TIMECLIP(msec_time);
 
     return js_NewNumberInRootedValue(cx, msec_time, vp);
 }
 
+/* 
+ * Read and convert decimal digits from s[*i] into *result
+ * while *i < limit. 
+ * 
+ * Succeed if any digits are converted. Advance *i only
+ * as digits are consumed.
+ */
+static JSBool
+digits(size_t *result, const jschar *s, size_t *i, size_t limit)
+{
+    size_t init = *i;
+    *result = 0;
+    while (*i < limit && 
+           ('0' <= s[*i] && s[*i] <= '9')) {
+        *result *= 10;
+        *result += (s[*i] - '0');
+        ++(*i);
+    }
+    return (*i != init);
+}
+
+/* 
+ * Read and convert decimal digits to the right of a decimal point,
+ * representing a fractional integer, from s[*i] into *result
+ * while *i < limit. 
+ * 
+ * Succeed if any digits are converted. Advance *i only
+ * as digits are consumed.
+ */
+static JSBool
+fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
+{
+    jsdouble factor = 0.1;
+    size_t init = *i;
+    *result = 0.0;
+    while (*i < limit && 
+           ('0' <= s[*i] && s[*i] <= '9')) {
+        *result += (s[*i] - '0') * factor;
+        factor *= 0.1;
+        ++(*i);
+    }
+    return (*i != init);
+}
+
+/* 
+ * Read and convert exactly n decimal digits from s[*i] 
+ * to s[min(*i+n,limit)] into *result. 
+ *
+ * Succeed if exactly n digits are converted. Advance *i only
+ * on success.
+ */
+static JSBool
+ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
+{
+    size_t init = *i;
+
+    if (digits(result, s, i, JS_MIN(limit, init+n)))
+        return ((*i - init) == n);
+    
+    *i = init;
+    return JS_FALSE;
+}
+
+/* 
+ * Parse a string in one of the date-time formats given by the W3C
+ * "NOTE-datetime" specification. These formats make up a restricted
+ * profile of the ISO 8601 format. Quoted here:
+ *
+ *   The formats are as follows. Exactly the components shown here
+ *   must be present, with exactly this punctuation. Note that the "T"
+ *   appears literally in the string, to indicate the beginning of the
+ *   time element, as specified in ISO 8601.
+ *
+ *   Any combination of the date formats with the time formats is
+ *   allowed, and also either the date or the time can be missing.
+ *
+ *   The specification is silent on the meaning when fields are
+ *   ommitted so the interpretations are a guess, but hopefully a
+ *   reasonable one. We default the month to January, the day to the
+ *   1st, and hours minutes and seconds all to 0. If the date is
+ *   missing entirely then we assume 1970-01-01 so that the time can
+ *   be aded to a date later. If the time is missing then we assume
+ *   00:00 UTC.  If the time is present but the time zone field is
+ *   missing then we use local time.
+ * 
+ * Date part:
+ *
+ *  Year:
+ *     YYYY (eg 1997)
+ *
+ *  Year and month:
+ *     YYYY-MM (eg 1997-07)
+ *
+ *  Complete date:
+ *     YYYY-MM-DD (eg 1997-07-16)
+ *
+ * Time part:
+ *
+ *  Hours and minutes:
+ *     Thh:mmTZD (eg T19:20+01:00)
+ * 
+ *  Hours, minutes and seconds:
+ *     Thh:mm:ssTZD (eg T19:20:30+01:00)
+ *
+ *  Hours, minutes, seconds and a decimal fraction of a second:
+ *     Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
+ *
+ * where:
+ *
+ *   YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
+ *   MM   = two-digit month (01=January, etc.)
+ *   DD   = two-digit day of month (01 through 31)
+ *   hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
+ *   mm   = two digits of minute (00 through 59)
+ *   ss   = two digits of second (00 through 59)
+ *   s    = one or more digits representing a decimal fraction of a second
+ *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
+ */
+
+static JSBool
+date_parseISOString(JSString *str, jsdouble *result)
+{
+    jsdouble msec;
+
+    const jschar *s;
+    size_t limit;
+    size_t i = 0;
+    int tzMul = 1;
+    int dateMul = 1;
+    size_t year = 1970;
+    size_t month = 1;
+    size_t day = 1;
+    size_t hour = 0;
+    size_t min = 0;
+    size_t sec = 0;
+    jsdouble frac = 0;
+    bool isLocalTime = JS_FALSE;
+    size_t tzHour = 0;
+    size_t tzMin = 0;
+
+#define PEEK(ch) (i < limit && s[i] == ch)
+
+#define NEED(ch)                                                     \
+    JS_BEGIN_MACRO                                                   \
+        if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
+    JS_END_MACRO 
+
+#define DONE_DATE_UNLESS(ch)                                            \
+    JS_BEGIN_MACRO                                                      \
+        if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
+    JS_END_MACRO 
+
+#define DONE_UNLESS(ch)                                            \
+    JS_BEGIN_MACRO                                                 \
+        if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
+    JS_END_MACRO 
+
+#define NEED_NDIGITS(n, field)                                      \
+    JS_BEGIN_MACRO                                                  \
+        if (!ndigits(n, &field, s, &i, limit)) { goto syntax; }     \
+    JS_END_MACRO 
+
+    str->getCharsAndLength(s, limit);
+
+    if (PEEK('+') || PEEK('-')) {
+        if (PEEK('-'))
+            dateMul = -1;
+        ++i;
+        NEED_NDIGITS(6, year);
+    } else if (!PEEK('T')) {
+        NEED_NDIGITS(4, year);
+    }
+    DONE_DATE_UNLESS('-');
+    NEED_NDIGITS(2, month);
+    DONE_DATE_UNLESS('-');
+    NEED_NDIGITS(2, day);
+
+ done_date:
+    DONE_UNLESS('T');
+    NEED_NDIGITS(2, hour);
+    NEED(':');
+    NEED_NDIGITS(2, min);
+
+    if (PEEK(':')) {
+        ++i;
+        NEED_NDIGITS(2, sec);
+        if (PEEK('.')) {
+            ++i;
+            if (!fractional(&frac, s, &i, limit))
+                goto syntax;
+        }
+    }
+
+    if (PEEK('Z')) {
+        ++i;
+    } else if (PEEK('+') || PEEK('-')) {
+        if (PEEK('-'))
+            tzMul = -1;
+        ++i;
+        NEED_NDIGITS(2, tzHour);
+        NEED(':');
+        NEED_NDIGITS(2, tzMin);
+    } else {
+        isLocalTime = JS_TRUE;
+    }
+
+ done:
+    if (year > 275943 // ceil(1e8/365) + 1970
+        || (month == 0 || month > 12)
+        || (day == 0 || day > DaysInMonth(year,month))
+        || hour > 24 
+        || ((hour == 24) && (min > 0 || sec > 0))
+        || min > 59 
+        || sec > 59
+        || tzHour > 23
+        || tzMin > 59) 
+        goto syntax;
+
+    if (i != limit)
+        goto syntax;
+
+    month -= 1; /* convert month to 0-based */
+
+    msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
+                             hour, min, sec,
+                             frac * 1000.0);;
+
+    if (isLocalTime) {
+        msec = UTC(msec);
+    } else {
+        msec -= ((tzMul) * ((tzHour * msPerHour) 
+                            + (tzMin * msPerMinute)));
+    }
+
+    if (msec < -8.64e15 || msec > 8.64e15)
+        goto syntax;
+
+    *result = msec;
+
+    return JS_TRUE;
+
+ syntax:
+    /* syntax error */
+    *result = 0;
+    return JS_FALSE;
+
+#undef PEEK
+#undef NEED
+#undef DONE_UNLESS
+#undef NEED_NDIGITS
+}
+
 static JSBool
 date_parseString(JSString *str, jsdouble *result)
 {
     jsdouble msec;
 
     const jschar *s;
     size_t limit;
     size_t i = 0;
@@ -643,16 +903,19 @@ date_parseString(JSString *str, jsdouble
     int c = -1;
     int n = -1;
     int tzoffset = -1;
     int prevc = 0;
     JSBool seenplusminus = JS_FALSE;
     int temp;
     JSBool seenmonthname = JS_FALSE;
 
+    if (date_parseISOString(str, result))
+        return JS_TRUE;
+
     str->getCharsAndLength(s, limit);
     if (limit == 0)
         goto syntax;
     while (i < limit) {
         c = s[i];
         i++;
         if (c <= ' ' || c == ',' || c == '-') {
             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
@@ -871,26 +1134,25 @@ date_parseString(JSString *str, jsdouble
     }
     mon -= 1; /* convert month to 0-based */
     if (sec < 0)
         sec = 0;
     if (min < 0)
         min = 0;
     if (hour < 0)
         hour = 0;
-    if (tzoffset == -1) { /* no time zone specified, have to use local */
-        jsdouble msec_time;
-        msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
-
-        *result = UTC(msec_time);
-        return JS_TRUE;
-    }
 
     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
-    msec += tzoffset * msPerMinute;
+
+    if (tzoffset == -1) { /* no time zone specified, have to use local */
+        msec = UTC(msec);
+    } else {
+        msec += tzoffset * msPerMinute;
+    }
+
     *result = msec;
     return JS_TRUE;
 
 syntax:
     /* syntax error */
     *result = 0;
     return JS_FALSE;
 }
new file mode 100644
--- /dev/null
+++ b/js/tests/ecma_5/Date/15.9.4.2.js
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is JavaScript Engine testing utilities.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Bruce Hoult
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var gTestfile = '15.9.4.2.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 430930;
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function iso(d){
+  return new Date(d).toISOString();
+}
+
+function check(s, millis){
+  description = "Date.parse('"+s+"') == '"+iso(millis)+"'";
+  expected = millis;
+  actual = Date.parse(s);
+  reportCompare(expected, actual, description);
+}
+
+function dd(year, month, day, hour, minute, second, millis){
+  return Date.UTC(year, month-1, day, hour, minute, second, millis);
+}
+
+function TZAtDate(d){
+  return d.getTimezoneOffset() * 60000;
+}
+
+function TZInMonth(month){
+  return TZAtDate(new Date(dd(2009,month,1,0,0,0,0)));
+}
+
+function test()
+{
+  enterFunc ('test');
+  printBugNumber(BUGNUMBER);
+ 
+  JanTZ = TZInMonth(1);
+  JulTZ = TZInMonth(7);
+  CurrTZ = TZAtDate(new Date());
+
+  // formats with explicit timezone
+  check("2009-07-23T19:53:21.001+12:00", dd(2009,7,23,7,53,21,1));
+  check("2009-07-23T19:53:21+12:00",     dd(2009,7,23,7,53,21,0));
+  check("2009-07-23T19:53+12:00",        dd(2009,7,23,7,53,0,0));
+
+  check("2009-07T19:53:21.001+12:00",    dd(2009,7,1,7,53,21,1));
+  check("2009-07T19:53:21+12:00",        dd(2009,7,1,7,53,21,0));
+  check("2009-07T19:53+12:00",           dd(2009,7,1,7,53,0,0));
+
+  check("2009T19:53:21.001+12:00",       dd(2009,1,1,7,53,21,1));
+  check("2009T19:53:21+12:00",           dd(2009,1,1,7,53,21,0));
+  check("2009T19:53+12:00",              dd(2009,1,1,7,53,0,0));
+
+  check("T19:53:21.001+12:00",           dd(1970,1,1,7,53,21,1));
+  check("T19:53:21+12:00",               dd(1970,1,1,7,53,21,0));
+  check("T19:53+12:00",                  dd(1970,1,1,7,53,0,0));
+
+  // formats without timezone uses the timezone as at that date
+  check("2009-07-23T19:53:21.001",       dd(2009,7,23,19,53,21,1)+JulTZ);
+  check("2009-07-23T19:53:21",           dd(2009,7,23,19,53,21,0)+JulTZ);
+  check("2009-07-23T19:53",              dd(2009,7,23,19,53,0,0)+JulTZ);
+
+  check("2009-07T19:53:21.001",          dd(2009,7,1,19,53,21,1)+JulTZ);
+  check("2009-07T19:53:21",              dd(2009,7,1,19,53,21,0)+JulTZ);
+  check("2009-07T19:53",                 dd(2009,7,1,19,53,0,0)+JulTZ);
+
+  check("2009T19:53:21.001",             dd(2009,1,1,19,53,21,1)+JanTZ);
+  check("2009T19:53:21",                 dd(2009,1,1,19,53,21,0)+JanTZ);
+  check("2009T19:53",                    dd(2009,1,1,19,53,0,0)+JanTZ);
+
+  check("T19:53:21.001",                 dd(1970,1,1,19,53,21,1)+CurrTZ);
+  check("T19:53:21",                     dd(1970,1,1,19,53,21,0)+CurrTZ);
+  check("T19:53",                        dd(1970,1,1,19,53,0,0)+CurrTZ);
+
+  // with no time at all assume UTC
+  check("2009-07-23",                    dd(2009,7,23,0,0,0,0));
+  check("2009-07",                       dd(2009,7,1,0,0,0,0));
+  check("2009",                          dd(2009,1,1,0,0,0,0));
+  check("",                              NaN);
+
+  // one field too big
+  check("2009-13-23T19:53:21.001+12:00", NaN);
+  check("2009-07-32T19:53:21.001+12:00", NaN);
+  check("2009-07-23T25:53:21.001+12:00", NaN);
+  check("2009-07-23T19:60:21.001+12:00", NaN);
+  check("2009-07-23T19:53:60.001+12:00", NaN);
+  check("2009-07-23T19:53:21.001+24:00", NaN);
+  check("2009-07-23T19:53:21.001+12:60", NaN);
+
+  // other month ends
+  check("2009-06-30T19:53:21.001+12:00", dd(2009,6,30,7,53,21,1));
+  check("2009-06-31T19:53:21.001+12:00", NaN);
+  check("2009-02-28T19:53:21.001+12:00", dd(2009,2,28,7,53,21,1));
+  check("2009-02-29T19:53:21.001+12:00", NaN);
+  check("2008-02-29T19:53:21.001+12:00", dd(2008,2,29,7,53,21,1));
+  check("2008-02-30T19:53:21.001+12:00", NaN);
+
+  // limits of representation
+  check("-271821-04-19T23:59:59.999Z", NaN);
+  check("-271821-04-20", -8.64e15);
+  check("+275760-09-13", 8.64e15);
+  check("+275760-09-13T00:00:00.001Z", NaN);
+
+  check("-269845-07-23T19:53:21.001+12:00", dd(-269845,7,23,7,53,21,1));
+  check("+273785-07-23T19:53:21.001+12:00", dd(273785,7,23,7,53,21,1));
+
+  // explicit UTC
+  check("2009-07-23T19:53:21.001Z", dd(2009,7,23,19,53,21,1));
+  check("+002009-07-23T19:53:21.001Z", dd(2009,7,23,19,53,21,1));
+
+  // different timezones
+  check("2009-07-23T19:53:21.001+12:00", dd(2009,7,23,7,53,21,1));
+  check("2009-07-23T00:53:21.001-07:00", dd(2009,7,23,7,53,21,1));
+
+  // 00:00 and 24:00
+  check("2009-07-23T00:00:00.000-07:00", dd(2009,7,23,7,0,0,0));
+  check("2009-07-23T24:00:00.000-07:00", dd(2009,7,24,7,0,0,0));
+
+  exitFunc ('test');
+}
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/js/tests/ecma_5/Date/shell.js
@@ -0,0 +1,1 @@
+gTestsubsuite='Date';
new file mode 100644
--- /dev/null
+++ b/js/tests/ecma_5/README
@@ -0,0 +1,1 @@
+ECMA 5
new file mode 100644
--- /dev/null
+++ b/js/tests/ecma_5/browser.js
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
new file mode 100644
--- /dev/null
+++ b/js/tests/ecma_5/shell.js
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+gTestsuite = 'ecma_5';
new file mode 100644
--- /dev/null
+++ b/js/tests/ecma_5/template.js
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is JavaScript Engine testing utilities.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var gTestfile = 'template.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 99999;
+var summary = '';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  enterFunc ('test');
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  reportCompare(expect, actual, summary);
+
+  exitFunc ('test');
+}