Simple JavaScript Date Formatting

This week, in anticipation of our alpha launch, Jason asked me to write a mechanism for giving a user the ability to embed one of our feeds on their webpage. You know the drill here; we give them a box of code to copy and paste onto their page, and then after that it just sort of magically works. Anyway, in the process of doing this, I was at a point where I wanted to format what the date and time would look like.

Jason and I do a pretty good job of talking about what's coming around the corner, and he'd mentioned to me in passing that one thing he'd like in the future is a mechanism to allow the user to customize what the time formatting looked like, so I wanted to make sure I planned for that in advance. So, this morning I took on the challenge of taking a JavaScript date object (created from an integer Unix timestamp) and formatting it.

I decided that the best use of time was probably just to write a function to take a date and output it in an arbitrary way, rather than hard-coding the exact formatting we were going to use to start. That would allow me to easily accept an arbitrary format later, and also allow Jason some flexibility if he didn't like how one of the formats looked when he saw the final page; he could just edit the format string rather than asking me to revisit the date formatter.

The next step was determining how to specify a date format. Despite being a Python developer, I actually think that PHP has one of the nicer date formatting libraries I've seen (the developers of Django seem to agree with me on this; they copied PHP's date formatting for their template filters). I therefore settled on PHP's date() function as the standard for the formatting strings I would understand, although I only implemented a subset of the format strings, at least for now.

The function is nothing special; it simply iterates over all the tokens I understand and does string replacements. After completing it, however, Jason and I both felt like this might be something that other JavaScript developers might be able to use. Therefore, we're offering it here for anyone to shamelessly copy as they see fit. And, of course, if anyone finds a bug, or has an improvement, or just wants to implement the formatting tokens I chose to skip, I'm all ears!

/**
 * Return a formatted date string 
 *
 * Arguments:
 *   - date: A JavaScript Date object; if you have a unix timestamp, you want "new Date(timestamp * 1000)"
 *   - format: The format string; this is a subset of PHP's date formatting strings,
 *        see http://www.php.net/manual/en/function.date.php for a reference
 *
 * Author and License:
 *   - author: Luke Sneeringer, FeedMagnet (luke@feedmagnet.com)
 *   - license: New BSD License (http://www.opensource.org/licenses/bsd-license.php)
 */
function formatDate(date, format) {
    // return a number as a string leading zeros when necessary
    function _pad(n, digits) {
        n = n.toString()
        while (n.length < digits) {
            n = '0' + n
        }
        return n
    }

    // the working response
    var response = format

    // escape the characters I supported
    var supported = 'djDNwlFmMnyYgGhHisaA'
    for (var i = 0; i < supported.length; i += 1) {
        var ch = supported.charAt(i)
        response = response.replace(ch, '%' + ch)
    }

    // day of the month
    if (format.indexOf('d') > -1 || format.indexOf('j') > -1) {
        response = response.replace('%j', date.getDate().toString())
        response = response.replace('%d', _pad(date.getDate(), 2))
    }

    // day of the week
    if (format.indexOf('D') > -1 || format.indexOf('N') > -1 || format.indexOf('w') > -1 || format.indexOf('l') > -1) {
        var d_short = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
        var d_long = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']

        response = response.replace('%w', date.getDay().toString())
        response = response.replace('%N', (date.getDay() + 1).toString())
        response = response.replace('%D', d_short[date.getDay()])
        response = response.replace('%l', d_long[date.getDay()])
    }

    // month
    if (format.indexOf('F') > -1 || format.indexOf('m') > -1 || format.indexOf('M') > -1 || format.indexOf('n') > -1) {
        var m_short = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        var m_long = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']

        response = response.replace('%F', m_long[date.getMonth()])
        response = response.replace('%m', _pad(date.getMonth() + 1, 2))
        response = response.replace('%M', m_short[date.getMonth()])
        response = response.replace('%n', (date.getMonth() + 1).toString())
    }

    // year
    if (format.indexOf('y') > -1 || format.indexOf('Y') > -1) {
        response = response.replace('%y', date.getFullYear().toString().substr(2))
        response = response.replace('%Y', date.getFullYear())
    }

    // hours
    if (format.indexOf('g') > -1 || format.indexOf('G') > -1 || format.indexOf('h') > -1 || format.indexOf('H') > -1) {
        var hours = date.getHours()
        response = response.replace('%G', hours.toString())
        response = response.replace('%H', _pad(hours, 2))

        // get 12-hour time
        if (hours == 0) { hours = 12 }
        if (hours > 12) { hours -= 12 }
        response = response.replace('%g', hours.toString())
        response = response.replace('%h', _pad(hours, 2))
    }

    // minutes
    if (format.indexOf('i') > -1) {
        response = response.replace('%i', _pad(date.getMinutes(), 2))
    }

    // seconds
    if (format.indexOf('s') > -1) {
        response = response.replace('%s', _pad(date.getSeconds(), 2))
    }

    // AM and PM
    if (format.indexOf('a') > -1 || format.indexOf('A') > -1) {
        var m = date.getHours() > 0 && date.getHours < 12 ? 'am' : 'pm'
        response = response.replace('%a', m)
        response = response.replace('%A', m.toUpperCase())
    }

    return response
}

Share This

We'd love your feedback on this post. Share it, or just drop us a line on Twitter: @feedmagnet.

by Luke on Sneeringer Nov. 19, 2009