Skip to content

Instantly share code, notes, and snippets.

@RienNeVaPlus
Last active August 23, 2023 00:20
Show Gist options
  • Save RienNeVaPlus/024de3431ae95546d60f2acce128a7e2 to your computer and use it in GitHub Desktop.
Save RienNeVaPlus/024de3431ae95546d60f2acce128a7e2 to your computer and use it in GitHub Desktop.
πŸ•€ dateDiff() - returns a detail object about the difference between two dates
/**
* β˜ƒ dateDiff "Snowman Carl" (http://stackoverflow.com/questions/13903897)
* Returns a detail object about the difference between two dates
*
* When providing custom units, provide them in descending order (eg week,day,hour; not hour,day,week)
*
* @param {Date} dateStart - date to compare to
* @param {Date|string} [dateEnd=new Date()] - second date, can be used as unit param instead
* @param {...string} [units=Object.keys(dateDiffDef)] - limits the returned object to provided keys
*/
export function dateDiff(
dateStart: Date,
dateEnd: Date | string = new Date,
...units: string[]
): {
[key: string]: number
} {
if(typeof dateEnd === 'string')
dateEnd = new Date();
let delta: number = Math.abs(dateStart.getTime() - dateEnd.getTime());
return (units.length ? units : Object.keys(dateDiffDef))
.reduce((res: object, key: string) => {
if(!dateDiffDef.hasOwnProperty(key))
throw new Error('Unknown unit in dateDiff: '+key);
res[key] = Math.floor(delta / dateDiffDef[key]);
delta -= res[key] * dateDiffDef[key];
return res;
}, {});
}
// default time units for dateDiff
export const dateDiffDef = {
millennium: 31536000000000,
century: 3153600000000,
decade: 315360000000,
year: 31536000000,
quarter: 7776000000,
month: 2592000000,
week: 604800000,
day: 86400000,
hour: 3600000,
minute: 60000,
second: 1000,
millisecond: 1
};
@RienNeVaPlus
Copy link
Author

Usage:

console.log(
	'Elapsed time since the sinking of the RMS Titanic:',
	dateDiff( new Date(1912, 3, 15) ), '\n',
	// { millennium: 0, century: 1, decade: 0, year: 6, quarter: 3, month: 2, 
	//  week: 1, day: 5, hour: 0, minute: 51, second: 27, millisecond: 327}

	'Years + months between JavaScript and TypeScript was firstly introduced:',
	dateDiff(new Date(1995, 10, 18), new Date(2012, 9, 1), 'year', 'month')
	// {year: 16, month: 10}
);

When using units, provide them in descending order (week, day, hour instead of hour, day, week).

@malarahfelipe
Copy link

Nice one πŸ‘

I used and formatted a little to be more type precise

image

Here's the actual code

// default time units for dateDiff
export const DATE_DIFF_DEF = Object.freeze({
	millennium:	31536000000000,
	century:	3153600000000,
	decade:		315360000000,
	year:		31536000000,
	quarter:	7776000000,
	month:		2592000000,
	week:		604800000,
	day:		86400000,
	hour:		3600000,
	minute:		60000,
	second:		1000,
	millisecond:1
});


export type DateDiffKey = keyof typeof DATE_DIFF_DEF
export type DateDifference = Record<DateDiffKey, number>

export const DATE_DIFF_KEYS = Object.freeze(Object.keys(DATE_DIFF_DEF)) as DateDiffKey[]

/**
 * β˜ƒ dateDiff "Snowman Carl" (http://stackoverflow.com/questions/13903897)
 * Returns a detail object about the difference between two dates
 *
 * When providing custom units, provide them in descending order (eg week,day,hour; not hour,day,week)
 *
 * @param {Date} dateStart - date to compare to
 * @param {Date|string} [dateEnd=new Date()] - second date, can be used as unit param instead
 * @returns {DateDifference}
 */
 export function dateDiff(
	dateStart:	Date,
	dateEnd:	Date | string = new Date()
): DateDifference {
	if(typeof dateEnd === 'string')
		dateEnd = new Date();

	let delta: number = Math.abs(dateStart.getTime() - dateEnd.getTime());

	return DATE_DIFF_KEYS
		.reduce<DateDifference>((res: DateDifference, key: DateDiffKey) => {
			res[key] = Math.floor(delta / DATE_DIFF_DEF[key]);
			delta -= res[key] * DATE_DIFF_DEF[key];
			return res;
		}, {} as DateDifference);
}

Thanks for it πŸ˜„

@perdanafm
Copy link

Nice one πŸ‘

I used and formatted a little to be more type precise

image

Hii, this is awesome, btw i wanna give some touches on this code, so the dateStart can be a string too, incase the input is a ISO String of Date.

export function dateDiff(
  dateStart: Date,
  dateEnd: Date | string = new Date()
): DateDifference {
  if (typeof dateEnd === 'string') dateEnd = new Date();
  if (typeof dateStart === 'string') dateStart = new Date(dateStart);

  let delta: number = Math.abs(dateStart.getTime() - dateEnd.getTime());

  return DATE_DIFF_KEYS.reduce<DateDifference>(
    (res: DateDifference, key: DateDiffKey) => {
      res[key] = Math.floor(delta / DATE_DIFF_DEF[key]);
      delta -= res[key] * DATE_DIFF_DEF[key];
      return res;
    },
    {} as DateDifference
  );
}

@marlonlom
Copy link

@perdanafm you must provide a start and and end date for a valid date range, im not sure why do you mention that the starting date could be defaulted to the current date.

In fact, if you are comparing the current date and a fixed date (i.e curr and a furute date) you must add the two for generating a valid range.

The only thing i could mention for fixing, is adding an exception for cases where the delta is a negative number and/or some of the dates are null or undefined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment