Unify JSC date and time formating functions
https://bugs.webkit.org/show_bug.cgi?id=92282

Reviewed by Geoffrey Garen.

Replace the existing functions for formating GregorianDateTime
with one single function. This removes some code duplications
in DatePrototype and is a preperation to fix encoding issues,
since we can add UChar* values to the resulting string now.

* runtime/DateConstructor.cpp:
(JSC::callDate):
* runtime/DateConversion.cpp:
(JSC::formatDateTime):
* runtime/DateConversion.h:
(JSC):
* runtime/DatePrototype.cpp:
(JSC::formateDateInstance):
(JSC::dateProtoFuncToString):
(JSC::dateProtoFuncToUTCString):
(JSC::dateProtoFuncToDateString):
(JSC::dateProtoFuncToTimeString):
(JSC::dateProtoFuncToGMTString):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@124817 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 6619beb..12e26dd 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,29 @@
+2012-08-06  Patrick Gansterer  <paroga@webkit.org>
+
+        Unify JSC date and time formating functions
+        https://bugs.webkit.org/show_bug.cgi?id=92282
+
+        Reviewed by Geoffrey Garen.
+
+        Replace the existing functions for formating GregorianDateTime
+        with one single function. This removes some code duplications
+        in DatePrototype and is a preperation to fix encoding issues,
+        since we can add UChar* values to the resulting string now.
+
+        * runtime/DateConstructor.cpp:
+        (JSC::callDate):
+        * runtime/DateConversion.cpp:
+        (JSC::formatDateTime):
+        * runtime/DateConversion.h:
+        (JSC):
+        * runtime/DatePrototype.cpp:
+        (JSC::formateDateInstance):
+        (JSC::dateProtoFuncToString):
+        (JSC::dateProtoFuncToUTCString):
+        (JSC::dateProtoFuncToDateString):
+        (JSC::dateProtoFuncToTimeString):
+        (JSC::dateProtoFuncToGMTString):
+
 2012-08-06  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Unreviewed. Fix make distcheck.
diff --git a/Source/JavaScriptCore/runtime/DateConstructor.cpp b/Source/JavaScriptCore/runtime/DateConstructor.cpp
index 5984bbf..e4f89dd 100644
--- a/Source/JavaScriptCore/runtime/DateConstructor.cpp
+++ b/Source/JavaScriptCore/runtime/DateConstructor.cpp
@@ -168,11 +168,7 @@
 {
     GregorianDateTime ts;
     msToGregorianDateTime(exec, currentTimeMS(), false, ts);
-    DateConversionBuffer date;
-    DateConversionBuffer time;
-    formatDate(ts, date);
-    formatTime(ts, time);
-    return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time));
+    return JSValue::encode(jsNontrivialString(exec, formatDateTime(ts, DateTimeFormatDateAndTime, false)));
 }
 
 CallType DateConstructor::getCallData(JSCell*, CallData& callData)
diff --git a/Source/JavaScriptCore/runtime/DateConversion.cpp b/Source/JavaScriptCore/runtime/DateConversion.cpp
index 706ffe9..6f365d0 100644
--- a/Source/JavaScriptCore/runtime/DateConversion.cpp
+++ b/Source/JavaScriptCore/runtime/DateConversion.cpp
@@ -1,95 +1,117 @@
 /*
- * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
- * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Patrick Gansterer <paroga@paroga.com>
  *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
  *
- * 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.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * Alternatively, the contents of this file may be used under the terms
- * of either the Mozilla Public License Version 1.1, found at
- * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
- * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
- * (the "GPL"), in which case the provisions of the MPL or the GPL are
- * applicable instead of those above.  If you wish to allow use of your
- * version of this file only under the terms of one of those two
- * licenses (the MPL or the GPL) and not to allow others to use your
- * version of this file under the LGPL, indicate your decision by
- * deletingthe provisions above and replace them with the notice and
- * other provisions required by the MPL or the GPL, as the case may be.
- * If you do not delete the provisions above, a recipient may use your
- * version of this file under any of the LGPL, the MPL or the GPL.
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "config.h"
 #include "DateConversion.h"
 
-#include "CallFrame.h"
-#include "JSDateMath.h"
-#include "JSObject.h"
-#include "ScopeChain.h"
 #include "UString.h"
-#include <wtf/StringExtras.h>
-#include <wtf/text/CString.h>
+#include <wtf/Assertions.h>
+#include <wtf/DateMath.h>
+#include <wtf/text/StringBuilder.h>
 
 using namespace WTF;
 
 namespace JSC {
 
-void formatDate(const GregorianDateTime &t, DateConversionBuffer& buffer)
+template<int width>
+static inline void appendNumber(StringBuilder& builder, int value)
 {
-    snprintf(buffer, DateConversionBufferSize, "%s %s %02d %04d",
-        weekdayName[(t.weekDay() + 6) % 7],
-        monthName[t.month()], t.monthDay(), t.year());
-}
-
-void formatDateUTCVariant(const GregorianDateTime &t, DateConversionBuffer& buffer)
-{
-    snprintf(buffer, DateConversionBufferSize, "%s, %02d %s %04d",
-        weekdayName[(t.weekDay() + 6) % 7],
-        t.monthDay(), monthName[t.month()], t.year());
-}
-
-void formatTime(const GregorianDateTime &t, DateConversionBuffer& buffer)
-{
-    int offset = abs(t.utcOffset());
-    char timeZoneName[70];
-    struct tm gtm = t;
-    strftime(timeZoneName, sizeof(timeZoneName), "%Z", &gtm);
-
-    if (timeZoneName[0]) {
-        snprintf(buffer, DateConversionBufferSize, "%02d:%02d:%02d GMT%c%02d%02d (%s)",
-            t.hour(), t.minute(), t.second(),
-            t.utcOffset() < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, timeZoneName);
-    } else {
-        snprintf(buffer, DateConversionBufferSize, "%02d:%02d:%02d GMT%c%02d%02d",
-            t.hour(), t.minute(), t.second(),
-            t.utcOffset() < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
+    int fillingZerosCount = width;
+    if (value < 0) {
+        builder.append('-');
+        value = -value;
+        --fillingZerosCount;
     }
+    String valueString = String::number(value);
+    fillingZerosCount -= valueString.length();
+    for (int i = 0; i < fillingZerosCount; ++i)
+        builder.append('0');
+    builder.append(valueString);
 }
 
-void formatTimeUTC(const GregorianDateTime &t, DateConversionBuffer& buffer)
+template<>
+void appendNumber<2>(StringBuilder& builder, int value)
 {
-    snprintf(buffer, DateConversionBufferSize, "%02d:%02d:%02d GMT", t.hour(), t.minute(), t.second());
+    ASSERT(0 <= value && value <= 99);
+    builder.append(static_cast<char>('0' + value / 10));
+    builder.append(static_cast<char>('0' + value % 10));
+}
+
+UString formatDateTime(const GregorianDateTime& t, DateTimeFormat format, bool asUTCVariant)
+{
+    bool appendDate = format & DateTimeFormatDate;
+    bool appendTime = format & DateTimeFormatTime;
+
+    StringBuilder builder;
+
+    if (appendDate) {
+        builder.append(weekdayName[(t.weekDay() + 6) % 7]);
+
+        if (asUTCVariant) {
+            builder.append(", ");
+            appendNumber<2>(builder, t.monthDay());
+            builder.append(' ');
+            builder.append(monthName[t.month()]);
+        } else {
+            builder.append(' ');
+            builder.append(monthName[t.month()]);
+            builder.append(' ');
+            appendNumber<2>(builder, t.monthDay());
+        }
+        builder.append(' ');
+        appendNumber<4>(builder, t.year());
+    }
+
+    if (appendDate && appendTime)
+        builder.append(' ');
+
+    if (appendTime) {
+        appendNumber<2>(builder, t.hour());
+        builder.append(':');
+        appendNumber<2>(builder, t.minute());
+        builder.append(':');
+        appendNumber<2>(builder, t.second());
+        builder.append(" GMT");
+
+        if (!asUTCVariant) {
+            int offset = abs(t.utcOffset()) / 60;
+            builder.append(t.utcOffset() < 0 ? '-' : '+');
+            appendNumber<2>(builder, offset / 60);
+            appendNumber<2>(builder, offset % 60);
+
+            struct tm gtm = t;
+            char timeZoneName[70];
+            strftime(timeZoneName, sizeof(timeZoneName), "%Z", &gtm);
+            if (timeZoneName[0]) {
+                builder.append(" (");
+                builder.append(timeZoneName);
+                builder.append(')');
+            }
+        }
+    }
+
+    return builder.toString().impl();
 }
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/DateConversion.h b/Source/JavaScriptCore/runtime/DateConversion.h
index 0b078cd..fd1a7eb 100644
--- a/Source/JavaScriptCore/runtime/DateConversion.h
+++ b/Source/JavaScriptCore/runtime/DateConversion.h
@@ -1,42 +1,25 @@
 /*
- * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
- * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Patrick Gansterer <paroga@paroga.com>
  *
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
  *
- * 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 of 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.
- *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef DateConversion_h
@@ -46,15 +29,15 @@
 
 namespace JSC {
 
-class ExecState;
+class UString;
 
-static const unsigned DateConversionBufferSize = 100;
-typedef char DateConversionBuffer[DateConversionBufferSize];
+enum DateTimeFormat {
+    DateTimeFormatDate = 1,
+    DateTimeFormatTime = 2,
+    DateTimeFormatDateAndTime = DateTimeFormatDate | DateTimeFormatTime
+};
 
-void formatDate(const GregorianDateTime&, DateConversionBuffer&);
-void formatDateUTCVariant(const GregorianDateTime&, DateConversionBuffer&);
-void formatTime(const GregorianDateTime&, DateConversionBuffer&);
-void formatTimeUTC(const GregorianDateTime&, DateConversionBuffer&);
+UString formatDateTime(const GregorianDateTime&, DateTimeFormat, bool asUTCVariant);
 
 } // namespace JSC
 
diff --git a/Source/JavaScriptCore/runtime/DatePrototype.cpp b/Source/JavaScriptCore/runtime/DatePrototype.cpp
index 7cbab04..363eec03 100644
--- a/Source/JavaScriptCore/runtime/DatePrototype.cpp
+++ b/Source/JavaScriptCore/runtime/DatePrototype.cpp
@@ -340,6 +340,23 @@
 
 #endif // !PLATFORM(MAC) && !PLATFORM(IOS)
 
+static EncodedJSValue formateDateInstance(ExecState* exec, DateTimeFormat format, bool asUTCVariant)
+{
+    JSValue thisValue = exec->hostThisValue();
+    if (!thisValue.inherits(&DateInstance::s_info))
+        return throwVMTypeError(exec);
+
+    DateInstance* thisDateObj = asDateInstance(thisValue);
+
+    const GregorianDateTime* gregorianDateTime = asUTCVariant
+        ? thisDateObj->gregorianDateTimeUTC(exec)
+        : thisDateObj->gregorianDateTime(exec);
+    if (!gregorianDateTime)
+        return JSValue::encode(jsNontrivialString(exec, "Invalid Date"));
+
+    return JSValue::encode(jsNontrivialString(exec, formatDateTime(*gregorianDateTime, format, asUTCVariant)));
+}
+
 // Converts a list of arguments sent to a Date member function into milliseconds, updating
 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
 //
@@ -513,38 +530,14 @@
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    if (!thisValue.inherits(&DateInstance::s_info))
-        return throwVMTypeError(exec);
-
-    DateInstance* thisDateObj = asDateInstance(thisValue); 
-
-    const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec);
-    if (!gregorianDateTime)
-        return JSValue::encode(jsNontrivialString(exec, "Invalid Date"));
-    DateConversionBuffer date;
-    DateConversionBuffer time;
-    formatDate(*gregorianDateTime, date);
-    formatTime(*gregorianDateTime, time);
-    return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time));
+    const bool asUTCVariant = false;
+    return formateDateInstance(exec, DateTimeFormatDateAndTime, asUTCVariant);
 }
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    if (!thisValue.inherits(&DateInstance::s_info))
-        return throwVMTypeError(exec);
-
-    DateInstance* thisDateObj = asDateInstance(thisValue); 
-
-    const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec);
-    if (!gregorianDateTime)
-        return JSValue::encode(jsNontrivialString(exec, "Invalid Date"));
-    DateConversionBuffer date;
-    DateConversionBuffer time;
-    formatDateUTCVariant(*gregorianDateTime, date);
-    formatTimeUTC(*gregorianDateTime, time);
-    return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time));
+    const bool asUTCVariant = true;
+    return formateDateInstance(exec, DateTimeFormatDateAndTime, asUTCVariant);
 }
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec)
@@ -577,34 +570,14 @@
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    if (!thisValue.inherits(&DateInstance::s_info))
-        return throwVMTypeError(exec);
-
-    DateInstance* thisDateObj = asDateInstance(thisValue); 
-
-    const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec);
-    if (!gregorianDateTime)
-        return JSValue::encode(jsNontrivialString(exec, "Invalid Date"));
-    DateConversionBuffer date;
-    formatDate(*gregorianDateTime, date);
-    return JSValue::encode(jsNontrivialString(exec, date));
+    const bool asUTCVariant = false;
+    return formateDateInstance(exec, DateTimeFormatDate, asUTCVariant);
 }
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    if (!thisValue.inherits(&DateInstance::s_info))
-        return throwVMTypeError(exec);
-
-    DateInstance* thisDateObj = asDateInstance(thisValue); 
-
-    const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec);
-    if (!gregorianDateTime)
-        return JSValue::encode(jsNontrivialString(exec, "Invalid Date"));
-    DateConversionBuffer time;
-    formatTime(*gregorianDateTime, time);
-    return JSValue::encode(jsNontrivialString(exec, time));
+    const bool asUTCVariant = false;
+    return formateDateInstance(exec, DateTimeFormatTime, asUTCVariant);
 }
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec)
@@ -676,20 +649,8 @@
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    if (!thisValue.inherits(&DateInstance::s_info))
-        return throwVMTypeError(exec);
-
-    DateInstance* thisDateObj = asDateInstance(thisValue); 
-
-    const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec);
-    if (!gregorianDateTime)
-        return JSValue::encode(jsNontrivialString(exec, "Invalid Date"));
-    DateConversionBuffer date;
-    DateConversionBuffer time;
-    formatDateUTCVariant(*gregorianDateTime, date);
-    formatTimeUTC(*gregorianDateTime, time);
-    return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time));
+    const bool asUTCVariant = true;
+    return formateDateInstance(exec, DateTimeFormatDateAndTime, asUTCVariant);
 }
 
 EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec)