If you've ever programmed in PHP, you've probably come across the date method, and maybe thought it was really neat. I certainly have, and since formatting and otherwise working with date strings in JavaScript isn't exactly easy (a lot of getSomeUnit), I thought I'd implement a generic solution once and for all, similar to the PHP one.

This is a pure client-side solution, and because of that, you can't always be sure it'll work correctly. Time and date issues are some of the more complex matters in JavaScript. Danny Goodman's article, JavaScript Date Object Techniques is hideously old (Netscape 3!), but still provides a valuable first look at date programming and it's pitfalls. Most important of all, this script in no way helps you determine a time, and set it. All this does, is, when given a Date object (where the date is always set), is to print it out, in a easier way, compared to what JavaScript usually allows.

Always test your script, to ensure it works in whatever browser you need it to work in. If you're using this code for sensitive code, where accurate dating is required, you are out of your mind. Use this for cosmetic features and otherwise non-critical code.

Over the years I've gotten alot of feedback on this code, such as bug fixes and improvements. I've tried to update the JavaScript file to contain all contributors, but if you are not listed, my apologies.

How it works

Using this function is very simple. You start by downloading, and linking the JavaScript file formatDate.js. You then need to create a date, that you want to format. Let's start of simple, and just format what time it is right now:

d = new Date();
alert(d.formatDate("h:i A")); // shows "01:52 am", or something
                              // similar depending on current time

All the various codes you can use, are listed in a table below, and they all depend on whatever date the date object has. So, if we want to create a specific date:

// notice that month and date are zero indexed (1st is 0)
d = new Date(1999, 11, 30);   // millenium new years eve
alert(d.formatDate("F dS"));  // shows "December 31st"

If you want a letter, not to be interpreted as a code, but instead as the mere letter, and you don't want to muck around with string building, you can simply escape it. But instead of normal escaping (using a single \), you need to escape here with two slashes \\. Like the letter F, is usually a code, for the month name (January, etc), if you instead just want "F", you just go: \\F.

d = new Date(1999,11,30);   // millennium new years eve

// shows "31st of December, the year 1999"
alert(d.formatDate("dS of F, \\t\\he \\ye\\a\\r Y"));  

Notice, how you you only need to escape those letters, that are actual codes. Therefore, the letters o,f and e doesn't need escaping.

For the full lowdown on how to set dates on Date objects I refer to the JavaScript 1.5 documentation on the Date object.

What is supported, and what isn't

Most of PHP's date method's switches are supported. Are few aren't.

Here is a full list of supported switches, unsupported ones, and ones, that I might include in the future. The switches are compared against PHP 5.1.3.

Supported switches Unsupported switches Currently unsupported, but might be included in the future
a, A, B, c, d, D, F, g, G, h, H, i, I (uppercase i), j, l (lowecase L), L, m, M, n, N, O, P, r, s, S, t, U, w, W, y, Y, z, Z T, e, o

And here is a full listing of that they do. Pretty much ripped from PHP's own page.

Switch Description Example of returned values
a Lowercase Ante meridiem and Post meridiem am or pm
A Uppercase Ante meridiem and Post meridiem AM or PM
B Swatch Internet time 000 through 999
c ISO 8601 date 2004-02-12T15:19:21+00:00
d Day of the month, 2 digits with leading zeros 01 through 31
D A textual representation of a day, three letters Mon through Sun
F A full textual representation of a month January through December
g 12-hour format of an hour without leading zeros 1 through 12
G 24-hour format of an hour without leading zeros 0 through 23
h 12-hour format of an hour with leading zeros 01 through 12
H 24-hour format of an hour with leading zeros 00 through 23
i Minutes with leading zeros 00 through 59
I (uppercase i) Whether or not the date is in daylights savings time 1 if Daylight Savings Time, 0 otherwise
j Day of the month without leading zeros 1 through 31
l (lowercase L) A full textual representation of the day of the week Sunday through Saturday
L Whether it's a leap year 1 if it is a leap year, 0 otherwise
m Numeric representation of a month, with leading zeros 01 through 12
M A short textual representation of a month, three letters Jan through Dec
n Numeric representation of a month, without leading zeros 1 through 12
N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
O Difference to Greenwich time (GMT) in hours Example: +0200
P Difference to Greenwich time (GMT) with colon between hours and minutes Example: +02:00
r RFC 822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200
s Seconds, with leading zeros 00 through 59
S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
t Number of days in the given month 28 through 31
U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1060266789
w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
W ISO-8601 week number of year 1 through 53
y A two-digit representation of a year Examples: 99 or 02
Y A full numeric representation of a year, 4 digits Examples: 1999 or 2002
z The day of the year 0 through 366
Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive -43200 through 43200

Notes

The script was written, with emphasis on compatibility. Thankfully, the Date object is part of Core JavaScript and is thus standardized in ECMAScript 3rd edition. Only methods, which are in that specification have been used. ECMAScript 3rd edition corresponds roughly to JavaScript 1.5, and to JScript 5.5. So that should cover the major newer browsers.

Nonetheless, older browsers might stumble a bit at places, especially problems with timezone offsets, and such things is hairy stuff, that one shouldn't really try and fix. For more on dates, and how browsers deal with it, Peter-Paul Koch, has a lot of good stuff on it.

You can pretty much count on the basic stuff, like getting days, date and year. What you might abstain from relying on (at least not without testing first), is things such as timezone offsets, and other similar switches.

Browser support

The following browsers have been tested with the script, and found to work as expect. If there's any problems, these have been noted. If you know any of these to be wrong, or a browser, where something fails, please email me, and tell me.

Browser Works Fails Notes
Internet Explorer 6.0 SP1 All None
Internet Explorer 5.0 (Win32) All None
Opera 7.20 Beta All None
Mozilla 1.4 All None
Netscape Navigator 4.08 All None
Netscape Navigator Gold 3.04 None All Dies horribly, since it's flavor of JavaScript is SO old, that it's very syntax isn't even supported. It would be possible (and not too hard), to bastardize the script, so it would work with Netscape 3, but in the name of decent code, I've opted not to do this.
WebTV viewer 2.5 None All null is returned in all cases.

How to localize a script

One of the major strengths of a this script, over PHP's date function, is that PHP's date function can only spit out English, this one can be easily modified, to spew out other languages, instead of English. And not just from English, into, say a Danish script, but it will be capable of both things at once. So if a user with English set as his language, comes in, he will get June, as well, "June", but a Dane will get it as "Juni" (Danish for June).

In the following section, I'll show how to make the script show Danish and English weekdays, instead of only English. The technique can be easily applied to all the other words just as easily (months, day suffix, and so forth).

In the top of the script, you will find this array entry:

var daysLong = ["Sunday", "Monday", "Tuesday", "Wednesday", 
                "Thursday", "Friday", "Saturday"];

Of course, we could just change that into Danish, and we'd be fine, but then there'd be no English of course. The solution is instead, to create two arrays, and rename them, so they show their language.

var daysLong_en = ["Sunday", "Monday", "Tuesday", "Wednesday", 
                   "Thursday", "Friday", "Saturday"];

var daysLong_dk = ["Søndag", "Mandag", "Tirsdag", "Onsdag", 
                   "Torsdag", "Fredag", "Lørdag"];

And we now need to change the function, which goes into that array, so that, before getting the weekday, it checks for language the user has set.

Your options for seeing what language a user has set are pretty limited and browser specific I'm afraid. Netscape has traditionally (since version 4, Mozilla supports this one too) supported the navigator.language attribute, while Internet Explorer, also since version 4, has supported navigator.browserLanguage. Opera supports both these ( though I do not know from what version).

You also need to write an array, with "supported" languages, so that in case the language could be detected, but it's not one that you've written text for, it'll just go back to English again.

var supportedLang = ["en", "dk"]; // for our little example, 
                                  // danish and english is it

The function that finds the week day is the following (the syntax may seem curious to you, but trust me, it works)

"l" : function () {
    // A full textual representation of the day of the week
    return daysLong[self.getDay()];
}

Which can then be rewritten into:

"l" : function () {
    var lang = "en"; // we default to english
    if (navigator) {
        if (navigator.language) {
            // netscape/mozilla
            lang = navigator.language;
        } else if (navigator.browserLanguage) {
            // internet explorer
            lang = navigator.browserLanguage;
        }
    }
       
    // if language isn't supported
    if (!supportedLang.exists(en)) lang = "en";

    var a = eval("daysLong_" + lang);
    return a[self.getDay()];
}

Of course, you can do the usual browser checking stuff, so you don't have to write that whole thing over and over. I'll leave it up to the reader, to make the script as small as possible :)

Test table

Here is a table, of how your browser handles the script. Use this table, to load up the various browser you want to check, and see if they work as expected.

The date used, is April 16, 1981, at 01:30 after midnight, that's my birthday, and the second date used is whatever it's right now. Of course, there's no "expected output" from the "now" time. If you're wondering why all the timezone stuff doesn't have a "expected", it's because the script always runs in YOUR timezone, and as such, that will show whatever timezone offset you currently have with UTC.

Switch Your browser (4/1981) Expected (4/1981) Your browser (now)
a am
A AM
B 020
d 16
D Thu
F April
g 1
G 1
h 01
H 01
i 30
j 16
l (lowercase L) Thursday
m 04
M Apr
n 4
O N/A
r Thu, 16 Apr 1981 01:30:00 +xxxx
s 00
S th
t 30
U 356225400
w 4
W 16
y 81
Y 1981
z 105

For a truly exercising test page, I've made a huge table, that tests all switches. It randomly generates dates, and you can then compare PHP's results, to the one generated by this script. Keep in mind, that results printed by my script will be localized, that is, numbers changed to fit your timezone. This is most clear in the switch r and in the I switch. In the case of I however, the PHP version will often be wrong, since it works of whatever the server believes. The script version will work off the JavaScript date algorithms (which may or not be localized). Still, if there's anything that isn't doing what I hope it's doing. Please let me know what I'm doing (wrong).