JavaScript Date Format

Update: The documentation below has been updated for the new Date Format 1.2. Get it now!

Although JavaScript provides a bunch of methods for getting and setting parts of a date object, it lacks a simple way to format dates and times according to a user-specified mask. There are a few scripts out there which provide this functionality, but I've never seen one that worked well for me… Most are needlessly bulky or slow, tie in unrelated functionality, use complicated mask syntaxes that more or less require you to read the documentation every time you want to use them, or don't account for special cases like escaping mask characters within the generated string.

When choosing which special mask characters to use for my JavaScript date formatter, I looked at PHP's date function and ColdFusion's discrete dateFormat and timeFormat functions. PHP uses a crazy mix of letters (to me at least, since I'm not a PHP programmer) to represent various date entities, and while I'll probably never memorize the full list, it does offer the advantages that you can apply both date and time formatting with one function, and that none of the special characters overlap (unlike ColdFusion where m and mm mean different things depending on whether you're dealing with dates or times). On the other hand, ColdFusion uses very easy to remember special characters for masks.

With my date formatter, I've tried to take the best features from both, and add some sugar of my own. It did end up a lot like the ColdFusion implementation though, since I've primarily used CF's mask syntax.

Before getting into further details, here are some examples of how this script can be used:

var now = new Date();

now.format("m/dd/yy");
// Returns, e.g., 6/09/07

// Can also be used as a standalone function
dateFormat(now, "dddd, mmmm dS, yyyy, h:MM:ss TT");
// Saturday, June 9th, 2007, 5:46:21 PM

// You can use one of several named masks
now.format("isoDateTime");
// 2007-06-09T17:46:21

// ...Or add your own
dateFormat.masks.hammerTime = 'HH:MM! "Can\'t touch this!"';
now.format("hammerTime");
// 17:46! Can't touch this!

// When using the standalone dateFormat function,
// you can also provide the date as a string
dateFormat("Jun 9 2007", "fullDate");
// Saturday, June 9, 2007

// Note that if you don't include the mask argument,
// dateFormat.masks.default is used
now.format();
// Sat Jun 09 2007 17:46:21

// And if you don't include the date argument,
// the current date and time is used
dateFormat();
// Sat Jun 09 2007 17:46:22

// You can also skip the date argument (as long as your mask doesn't
// contain any numbers), in which case the current date/time is used
dateFormat("longTime");
// 5:46:22 PM EST

// And finally, you can convert local time to UTC time. Either pass in
// true as an additional argument (no argument skipping allowed in this case):
dateFormat(now, "longTime", true);
now.format("longTime", true);
// Both lines return, e.g., 10:46:21 PM UTC

// ...Or add the prefix "UTC:" to your mask.
now.format("UTC:h:MM:ss TT Z");
// 10:46:21 PM UTC

Following are the special characters supported. Any differences in meaning from ColdFusion's dateFormat and timeFormat functions are noted.

Mask Description
d Day of the month as digits; no leading zero for single-digit days.
dd Day of the month as digits; leading zero for single-digit days.
ddd Day of the week as a three-letter abbreviation.
dddd Day of the week as its full name.
m Month as digits; no leading zero for single-digit months.
mm Month as digits; leading zero for single-digit months.
mmm Month as a three-letter abbreviation.
mmmm Month as its full name.
yy Year as last two digits; leading zero for years less than 10.
yyyy Year represented by four digits.
h Hours; no leading zero for single-digit hours (12-hour clock).
hh Hours; leading zero for single-digit hours (12-hour clock).
H Hours; no leading zero for single-digit hours (24-hour clock).
HH Hours; leading zero for single-digit hours (24-hour clock).
M Minutes; no leading zero for single-digit minutes.
Uppercase M unlike CF timeFormat's m to avoid conflict with months.
MM Minutes; leading zero for single-digit minutes.
Uppercase MM unlike CF timeFormat's mm to avoid conflict with months.
s Seconds; no leading zero for single-digit seconds.
ss Seconds; leading zero for single-digit seconds.
l or L Milliseconds. l gives 3 digits. L gives 2 digits.
t Lowercase, single-character time marker string: a or p.
No equivalent in CF.
tt Lowercase, two-character time marker string: am or pm.
No equivalent in CF.
T Uppercase, single-character time marker string: A or P.
Uppercase T unlike CF's t to allow for user-specified casing.
TT Uppercase, two-character time marker string: AM or PM.
Uppercase TT unlike CF's tt to allow for user-specified casing.
Z US timezone abbreviation, e.g. EST or MDT. With non-US timezones or in the Opera browser, the GMT/UTC offset is returned, e.g. GMT-0500
No equivalent in CF.
o GMT/UTC timezone offset, e.g. -0500 or +0230.
No equivalent in CF.
S The date's ordinal suffix (st, nd, rd, or th). Works well with d.
No equivalent in CF.
'…' or "…" Literal character sequence. Surrounding quotes are removed.
No equivalent in CF.
UTC: Must be the first four characters of the mask. Converts the date from local time to UTC/GMT/Zulu time before applying the mask. The "UTC:" prefix is removed.
No equivalent in CF.

And here are the named masks provided by default (you can easily change these or add your own):

Name Mask Example
default ddd mmm dd yyyy HH:MM:ss Sat Jun 09 2007 17:46:21
shortDate m/d/yy 6/9/07
mediumDate mmm d, yyyy Jun 9, 2007
longDate mmmm d, yyyy June 9, 2007
fullDate dddd, mmmm d, yyyy Saturday, June 9, 2007
shortTime h:MM TT 5:46 PM
mediumTime h:MM:ss TT 5:46:21 PM
longTime h:MM:ss TT Z 5:46:21 PM EST
isoDate yyyy-mm-dd 2007-06-09
isoTime HH:MM:ss 17:46:21
isoDateTime yyyy-mm-dd'T'HH:MM:ss 2007-06-09T17:46:21
isoUtcDateTime UTC:yyyy-mm-dd'T'HH:MM:ss'Z' 2007-06-09T22:46:21Z

A couple issues:

  • In the unlikely event that there is ambiguity in the meaning of your mask (e.g., m followed by mm, with no separating characters), put a pair of empty quotes between your metasequences. The quotes will be removed automatically.
  • If you need to include literal quotes in your mask, the following rules apply:
    • Unpaired quotes do not need special handling.
    • To include literal quotes inside masks which contain any other quote marks of the same type, you need to enclose them with the alternative quote type (i.e., double quotes for single quotes, and vice versa). E.g., date.format('h "o\'clock, y\'all!"') returns "6 o'clock, y'all". This can get a little hairy, perhaps, but I doubt people will really run into it that often. The previous example can also be written as date.format("h") + "o'clock, y'all!".

Here's the code:

/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */

var dateFormat = function () {
	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
			val = String(val);
			len = len || 2;
			while (val.length < len) val = "0" + val;
			return val;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask, utc) {
		var dF = dateFormat;

		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
			mask = date;
			date = undefined;
		}

		// Passing date through Date applies Date.parse, if necessary
		date = date ? new Date(date) : new Date;
		if (isNaN(date)) throw SyntaxError("invalid date");

		mask = String(dF.masks[mask] || mask || dF.masks["default"]);

		// Allow setting the utc argument via the mask
		if (mask.slice(0, 4) == "UTC:") {
			mask = mask.slice(4);
			utc = true;
		}

		var	_ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
				d:    d,
				dd:   pad(d),
				ddd:  dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m:    m + 1,
				mm:   pad(m + 1),
				mmm:  dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy:   String(y).slice(2),
				yyyy: y,
				h:    H % 12 || 12,
				hh:   pad(H % 12 || 12),
				H:    H,
				HH:   pad(H),
				M:    M,
				MM:   pad(M),
				s:    s,
				ss:   pad(s),
				l:    pad(L, 3),
				L:    pad(L > 99 ? Math.round(L / 10) : L),
				t:    H < 12 ? "a"  : "p",
				tt:   H < 12 ? "am" : "pm",
				T:    H < 12 ? "A"  : "P",
				TT:   H < 12 ? "AM" : "PM",
				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

		return mask.replace(token, function ($0) {
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
}();

// Some common format strings
dateFormat.masks = {
	"default":      "ddd mmm dd yyyy HH:MM:ss",
	shortDate:      "m/d/yy",
	mediumDate:     "mmm d, yyyy",
	longDate:       "mmmm d, yyyy",
	fullDate:       "dddd, mmmm d, yyyy",
	shortTime:      "h:MM TT",
	mediumTime:     "h:MM:ss TT",
	longTime:       "h:MM:ss TT Z",
	isoDate:        "yyyy-mm-dd",
	isoTime:        "HH:MM:ss",
	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
	return dateFormat(this, mask, utc);
};

Download it here (1.2 KB when minified and gzipped).

Note that the day and month names can be changed (for internationalization or other purposes) by updating the dateFormat.i18n object.

If you have any suggestions or find any issues, lemme know.


Want to learn about aphantasisa and hyperphantasia, the Shen Yun cult, or unmasking cult leader Karen Zerby? Check out my new blog at Life After Tech.

380 thoughts on “JavaScript Date Format”

  1. I have some dates in json file that are in isoDateTime format (i.e. 2007-08-10T11:56:06). In my javascript I am formatting these dates using the following:
    dateFormat(new Date(data._source.submissionTime),”longDate”);

    data._source.submissionTime resolves to date you see above (2007-08-10T11:56:06)

    Firefox and Chrome handle this just fine, however IE 8 is choking on the following line in your script and complaining that exception is not caught:

    if (isNaN(date)) throw SyntaxError(“invalid date”);

    Any thoughts on why IE is unable to handle this?

  2. The other thing I forgot mention above is that if do replace the hyphens with slash (as was suggested in previous posts), then IE works by Firefox and Chrome break 🙂

  3. Just written something similar, noticed a complicated looking zero pad function up there, here’s the one I just wrote:

    function zeroPad(number) {
    if (number < 10) {
    return '0' + number;
    }
    else {
    return '' + number;
    }
    }

  4. Steven, thank You very much!
    Now I dont have any problem with date/time formatting in javascript!
    Best regards, Vasily.

  5. Ciao,
    potresti indicare anche l’html che va inserito per visualizzare l’orologio?

  6. I’m trying to subtract one time from another and cannot seem to get it to display.

    Here is what I’m currently trying:

    var end = dateFormat(event.end, “isoTime”);
    var start = dateFormat(event.start, “isoTime”);
    var total = (end – start);

  7. First, I want to thank you for creating this library. I have been using your script without any problems on FF 3+. However, I have run into some difficulties on Chrome and Safari. I create a new date using an input like this one:

    var localDate = new Date(“2012-09-05T09:30″);

    and latter on my code I use the UTC mask

    console.log(dateFormat(localDate,”isoUtcDateTime”,true));

    on FF i get this:
    2012-09-05T13:30:00Z

    on Chrome 21 and Safari 6 I get this:
    2012-09-05T09:30:00Z

    Any ideas as why this is happening?
    Thanks

  8. following is code to remove ordinal of date IT is running successfully:

    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.regex.Pattern;
    import java.util.regex.Matcher;

    public class patrn {

    private static String deleteOrdinal(String dateString) {
    Pattern p1 = Pattern.compile(“([0-9]+)(st|nd|rd|th)”);
    Matcher m = p1.matcher(dateString);
    while (m.find()) {
    dateString = dateString.replaceAll(Matcher.quoteReplacement(m.group(0)), m.group(1));
    }
    return dateString;
    }

    public static void main(String[] args) {

    String dateString=”August 21st, 2012″;
    SimpleDateFormat sdf = new SimpleDateFormat(“MMMM dd, yyyy”);
    Date emp1joinDate = null;
    try {
    emp1joinDate=sdf.parse(deleteOrdinal(dateString));
    } catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    }

    }

  9. Excellent! Just when I was getting at the huge mess in my code … now with your library it’s clean 1 line of code!

    Keep up the good work!

  10. // In case anyone ever need czech i18n strings
    dateFormat.i18n = {
    dayNames: [“Ne”, “Po”, “Út”, “St”, “Čt”, “Pá”, “So”, “Neděle”, “Pondělí”, “Úterý”, “Středa”, “Čtvrtek”, “Pátek”, “Sobota”],
    monthNames: [“Led”, “Únr”, “Bře”, “Dub”, “Kvě”, “Čvn”, “Čvc”, “Srp”, “Zář”, “Říj”, “Lis”, “Pro”, “Leden”, “Únor”, “Březen”, “Duben”, “Květen”, “Červen”, “Červenec”, “Srpen”, “Září”, “Říjen”, “Listopad”, “Prosinec”]
    };

  11. I had one little issue when formatting the date output with padded hours, minutes, seconds, and AM and PM. If a number did need to be padded it actually ended up being repeated. For example if I specified the form seconds format to be ss and the time was currently 9 seconds, I would get 99 for the second count. If I formatted with the time TT asking for AM or PM, would get AA in the morning and PP at night.

    I had to make one little change to the regex in the first line of the dateFormat function to fix this issue.

    Here is the original
    var dateFormat = function () {
    var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|”[^”]*”|'[^’]*’/g,

    Here is the change I made:
    var dateFormat = function () {
    var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt]{1,2})\1?|[LloSZ]|”[^”]*”|'[^’]*’/g,

    I just added the {1,2} after the [HhMsTt] and now it works great!
    Thanks so much for a great date formatting package!

  12. Hi im using your code on a SharePoint site but for some reason i could not add the file using the script tag of a web part when i do that and tried to use any format i just got in response a lot of weird characters, i copy all the code and append it at the end of another js file and it works as normal, i tried the file using jsfiddler and works just good have you hear of any problems while using your file on SharePoint before?

  13. I have found and corrected a defect in the code as of today. When running this code in the Firefox v 20.0 Scratchpad, I ran into the following exception:

    Exception: dF.masks is undefined
    dateFormat@Scratchpad/1:26
    format@Scratchpad/1:110
    @Scratchpad/1:118

    I corrected this issue by changing the line:
    var dF = dateFormat;
    to
    var dF = this.dateFormat;

    -=> Gregg <=-

  14. Thanks for this, but I think there’s a bug with millisecond display when running on Firefox 20.0 on OS X 10.6.8 (and probably other platforms).

    The problem is line 39:

    date = date ? new Date(date) : new Date;

    It seems that this sets the number of milliseconds to zero on the created Date. Looking at the Date() doc, the constructor can’t take a Date as an argument, but a string, so it converts the initial Date to a string using Date.toString(), which doesn’t include milliseconds. The new Date is then built without milliseconds.

    Commenting out the line is a quick fix.

    Thanks again,
    Dan

  15. Please Help me

    var txt = 04/04;
    dateFormat(var, “dd/mm/yyyy”);

    why result : 04/04/2001
    should : 04/04/2013

    thanks

  16. I am trying to format a date that is 5 hours back (Unix TS = 1378470618) converted shows Fri, 06 Sep 2013 12:30:18 GMT (5 hrs back)

    When I use the date formatter like :

    dateFormat(start, “yyyymmddhhMM”) where start is 1378470618, the result shows Start Formatted 197001160554
    end = now timestamp and it formats -> End Formatted 201309060130

    Any idea why?

Leave a Reply

Your email address will not be published. Required fields are marked *