formatDate = function()

in globalize/globalize.ts [455:691]


formatDate = function (value, format, culture) {
    var cal = culture.calendar,
        convert = cal.convert;

    if (!format || !format.length || format === "i") {
        var ret;
        if (culture && culture.name.length) {
            if (convert) {
                // non-gregorian calendar, so we cannot use built-in toLocaleString()
                ret = formatDate(value, cal.patterns.F, culture);
            }
            else {
                var eraDate = new Date(value.getTime()),
                    era = getEra(value, cal.eras);
                eraDate.setFullYear(getEraYear(value, cal, era));
                ret = eraDate.toLocaleString();
            }
        }
        else {
            ret = value.toString();
        }
        return ret;
    }

    var eras = cal.eras,
        sortable = format === "s";
    format = expandFormat(cal, format);

    // Start with an empty string
    ret = [];
    var hour,
        zeros = ["0", "00", "000"],
        foundDay,
        checkedDay,
        dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
        quoteCount = 0,
        tokenRegExp = getTokenRegExp(),
        converted;

    function padZeros(num, c) {
        var r, s = num + "";
        if (c > 1 && s.length < c) {
            r = (zeros[c - 2] + s);
            return r.substr(r.length - c, c);
        }
        else {
            r = s;
        }
        return r;
    }

    function hasDay() {
        if (foundDay || checkedDay) {
            return foundDay;
        }
        foundDay = dayPartRegExp.test(format);
        checkedDay = true;
        return foundDay;
    }

    function getPart(date, part) {
        if (converted) {
            return converted[part];
        }
        switch (part) {
            case 0: return date.getFullYear();
            case 1: return date.getMonth();
            case 2: return date.getDate();
        }
    }

    if (!sortable && convert) {
        converted = convert.fromGregorian(value);
    }

    for (; ;) {
        // Save the current index
        var index = tokenRegExp.lastIndex,
            // Look for the next pattern
            ar = tokenRegExp.exec(format);

        // Append the text before the pattern (or the end of the string if not found)
        var preMatch = format.slice(index, ar ? ar.index : format.length);
        quoteCount += appendPreOrPostMatch(preMatch, ret);

        if (!ar) {
            break;
        }

        // do not replace any matches that occur inside a string literal.
        if (quoteCount % 2) {
            ret.push(ar[0]);
            continue;
        }

        var current = ar[0],
            clength = current.length;

        switch (current) {
            case "ddd":
            //Day of the week, as a three-letter abbreviation
            case "dddd":
                // Day of the week, using the full name
                var names = (clength === 3) ? cal.days.namesAbbr : cal.days.names;
                ret.push(names[value.getDay()]);
                break;
            case "d":
            // Day of month, without leading zero for single-digit days
            case "dd":
                // Day of month, with leading zero for single-digit days
                foundDay = true;
                ret.push(
                    padZeros(getPart(value, 2), clength)
                );
                break;
            case "MMM":
            // Month, as a three-letter abbreviation
            case "MMMM":
                // Month, using the full name
                var part = getPart(value, 1);
                ret.push(
                    (cal.monthsGenitive && hasDay())
                        ?
                        cal.monthsGenitive[clength === 3 ? "namesAbbr" : "names"][part]
                        :
                        cal.months[clength === 3 ? "namesAbbr" : "names"][part]
                );
                break;
            case "M":
            // Month, as digits, with no leading zero for single-digit months
            case "MM":
                // Month, as digits, with leading zero for single-digit months
                ret.push(
                    padZeros(getPart(value, 1) + 1, clength)
                );
                break;
            case "y":
            // Year, as two digits, but with no leading zero for years less than 10
            case "yy":
            // Year, as two digits, with leading zero for years less than 10
            case "yyyy":
                // Year represented by four full digits
                part = converted ? converted[0] : getEraYear(value, cal, getEra(value, eras), sortable);
                if (clength < 4) {
                    part = part % 100;
                }
                ret.push(
                    padZeros(part, clength)
                );
                break;
            case "h":
            // Hours with no leading zero for single-digit hours, using 12-hour clock
            case "hh":
                // Hours with leading zero for single-digit hours, using 12-hour clock
                hour = value.getHours() % 12;
                if (hour === 0) hour = 12;
                ret.push(
                    padZeros(hour, clength)
                );
                break;
            case "H":
            // Hours with no leading zero for single-digit hours, using 24-hour clock
            case "HH":
                // Hours with leading zero for single-digit hours, using 24-hour clock
                ret.push(
                    padZeros(value.getHours(), clength)
                );
                break;
            case "m":
            // Minutes with no leading zero for single-digit minutes
            case "mm":
                // Minutes with leading zero for single-digit minutes
                ret.push(
                    padZeros(value.getMinutes(), clength)
                );
                break;
            case "s":
            // Seconds with no leading zero for single-digit seconds
            case "ss":
                // Seconds with leading zero for single-digit seconds
                ret.push(
                    padZeros(value.getSeconds(), clength)
                );
                break;
            case "t":
            // One character am/pm indicator ("a" or "p")
            case "tt":
                // Multicharacter am/pm indicator
                part = value.getHours() < 12 ? (cal.AM ? cal.AM[0] : " ") : (cal.PM ? cal.PM[0] : " ");
                ret.push(clength === 1 ? part.charAt(0) : part);
                break;
            case "f":
            // Deciseconds
            case "ff":
            // Centiseconds
            case "fff":
                // Milliseconds
                ret.push(
                    padZeros(value.getMilliseconds(), 3).substr(0, clength)
                );
                break;
            case "z":
            // Time zone offset, no leading zero
            case "zz":
                // Time zone offset with leading zero
                hour = value.getTimezoneOffset() / 60;
                ret.push(
                    (hour <= 0 ? "+" : "-") + padZeros(Math.floor(Math.abs(hour)), clength)
                );
                break;
            case "zzz":
                // Time zone offset with leading zero
                hour = value.getTimezoneOffset() / 60;
                ret.push(
                    (hour <= 0 ? "+" : "-") + padZeros(Math.floor(Math.abs(hour)), 2)
                    // Hard coded ":" separator, rather than using cal.TimeSeparator
                    // Repeated here for consistency, plus ":" was already assumed in date parsing.
                    + ":" + padZeros(Math.abs(value.getTimezoneOffset() % 60), 2)
                );
                break;
            case "g":
            case "gg":
                if (cal.eras) {
                    ret.push(
                        cal.eras[getEra(value, eras)].name
                    );
                }
                break;
            case "/":
                ret.push(cal["/"]);
                break;
            default:
                throw "Invalid date format pattern \'" + current + "\'.";
        }
    }
    return ret.join("");
};