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;
};