Skip to content

Instantly share code, notes, and snippets.

@bseddon
Last active February 20, 2021 01:48
Show Gist options
  • Save bseddon/9c2449bf7bac257c226189e52710c4d0 to your computer and use it in GitHub Desktop.
Save bseddon/9c2449bf7bac257c226189e52710c4d0 to your computer and use it in GitHub Desktop.
Format dates in JavaScript

With the Intl object JavaScript is awash with functions to format dates so why is another one needed? The functions in Intl are generic and address uses cases where it's important that dates are formatted in a way that is commonly recognized by users within a locale. But there are many cases where date formatting is needed that has nothing to do with locales. My specific case is to be able to output date information that is consistent with the XML date types:

QName Format
xs:time HH-MM-SS
xs:gYearMonth YYYY-MM
xs:gYear YYYY
xs:gMonthDay --MM-DD
xs:gDay ---DD
xs:gMonth --MM

Of course there are other similar cases so it will be great to do something like:

console.log( new Date().format('YYYY-MM-DD') );

It seems the standards setter think that simple formatting should be done using string concatenation. But then you have to remember that Date.getMonth() is zero based and that the numbers need to be padded with leading zeros. My start point was this simple-minded approach:

Date.protocol.getYYYYMMDD = () => this.toISOString().slice(0, 10);
console.log( new Date().getYYYYMMDD() );

Straight-forward but not flexible enough so here's something more flexible but still simple and small - just 40 lines and 1.8Kb uncommpressed. This makes it easy to choose your own formatting such as:

new Date().parts.format('YYYY-MM-DD');
new Date().parts.format('HH-II-SS');
new Date().parts.format('YYYY-MM');
new Date().parts.format('--MM-DD');
new Date().parts.format('---DD');
Specifier Comment
YYYY A full year such as 2021
CC The century part of the year
YY The decade and unit part of the year
MM Minutes padded with a leading zero if needed
M Minuted not padded
DD Day of the month padded with a leading zero if needed
D Day of the month not padded
HH Hour of the day padded with a leading zero if needed.
H Hour of the day not padded
II Minutes padded with a leading zero if needed
I Minuted not padded
SS Seconds padded with a leading zero if needed
S Seconds not padded
MS Milliseconds padded with two leading zeros if needed
TZ The timezone of the date
AP 'AM' or 'PM' depending on the hour when not using twenty four hour digits

The format specifiers are not case sensitive. The 'parts' function added to the Date object takes an optional parameter to indicate whether 24 or 12 hours should be used - the default is to use 24 hours.

/**
 * Allows a user to access additional date formatting options
 * @param {boolean} [twentyFourHours ]
 */
Date.prototype.parts = function( twentyFourHours = true )
{
	const elements = { 
		YYYY: 'getFullYear', YY: 'getYear', CC: 'getCentury', MM: 'getFullMonth', M: 'getMonth', DD: 'getFullDate', D: 'getDate', AP: 'getAMPM',
		HH: 'getFullHours', H: 'getHours', II: 'getFullMinutes', I: 'getMinutes', SS: 'getFullSeconds', S: 'getSeconds', MS: 'getMilliseconds', TZ: 'getTimezone'
	};
	const full = (str) => ('0' + str).slice(-2);
	const date = this;
	const obj = {
		getCentury:		() => date.getFullYear().toString().slice(0,2),
		getYear:		() => date.getFullYear().toString().slice(2,2),
		getFullMonth:	() => full( obj.getMonth() ),
		getMonth:		() => date.getMonth() + 1,
		getFullDate:	() => full( date.getDate() ),
		getFullHours:	() => full( ( () => { const hours = date.getHours(); return twentyFourHours ? hours : ( hours % 12 ? ( hours % 12 ) : 12 ); } )() ),
		getFullMinutes:	() => full( date.getMinutes()),
		getFullSeconds:	() => full( date.getSeconds()),
		getTimezone:	() => { const offset = date.getTimezoneOffset(); return offset ? '+' + ( '0000' + offset ).slice(-5) : 'Z'; },
		getAMPM:		() => date.getHours() >= 12 ? 'PM': 'AM',
		getYYYYMMDD:	() => '',
		format:			() => '',
		getTime:		() => ''
	};
	const format = function( formatStr )
	{
		formatStr = formatStr.toUpperCase();
		const result = Object.keys( elements ).reduce( function(formatStr, key)
		{
			let fn = this[ elements[ key ] ] ? this[ elements[ key ] ]() : undefined;
			if ( ! fn ) fn = date[ elements[ key ] ] ? date[ elements[ key ] ]() : undefined;
			if ( fn )
				formatStr = formatStr.replace( key, fn ); 
			return formatStr;
		}.bind( obj ), formatStr );
		return result;
	};
	obj['getYYYYMMDD'] = function() { return format('YYYY-MM-DD'); };
	obj['format'] = function( formatStr ) { return format( formatStr ); };
	obj['getTime'] = () => format('HH:II:SS' + ( twentyFourHours ? '' : ' AP' ) );
	return obj;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment