Skip to content

Instantly share code, notes, and snippets.

@obedparla
Last active November 20, 2019 17:50
Show Gist options
  • Save obedparla/bc54f59bb325a34edaee4d6ead1604a5 to your computer and use it in GitHub Desktop.
Save obedparla/bc54f59bb325a34edaee4d6ead1604a5 to your computer and use it in GitHub Desktop.
Human readable dates
// *** Usage ***
// This will show timeAgo up to 1 week (2 hours ago, 4 days ago, etc) up to a week.
// Leave oneWeek out and the default is 3 days.
const refDate = new Date('Wed Nov 20 2019');
humanReadableDate(new Date(), refDate, oneWeek);
// Same as above but without abreviated months
longHumanReadableDate(refDate)
const dateOptions = {
year: 'numeric',
month: 'short',
day: 'numeric',
};
// Returns a human readable date such as "2 days ago", "1 week ago", etc.
const timeAgo = (date, refDate) => {
// Date Formats must be be ordered smallest -> largest and must end in a format with ceiling of null
const dateFormats = {
past: [
{ ceiling: 60, text: '$seconds seconds ago' },
{ ceiling: 3600, text: '$minutes minutes ago' },
{ ceiling: 86400, text: '$hours hours ago' },
{ ceiling: 2629744, text: '$days days ago' },
{ ceiling: 31556926, text: '$months months ago' },
{ ceiling: null, text: '$years years ago' },
],
future: [
{ ceiling: 60, text: 'in $seconds seconds' },
{ ceiling: 3600, text: 'in $minutes minutes' },
{ ceiling: 86400, text: 'in $hours hours' },
{ ceiling: 2629744, text: 'in $days days' },
{ ceiling: 31556926, text: 'in $months months' },
{ ceiling: null, text: 'in $years years' },
],
};
// Time units must be be ordered largest -> smallest
const timeUnits = [
[31556926, 'years'],
[2629744, 'months'],
[86400, 'days'],
[3600, 'hours'],
[60, 'minutes'],
[1, 'seconds'],
];
const newDate = new Date(date);
const newRefDate = refDate ? new Date(refDate) : new Date();
let secondsDifference = (newRefDate - newDate) / 1000;
let tense = 'past';
if (secondsDifference < 0) {
tense = 'future';
secondsDifference = 0 - secondsDifference;
}
function getFormat() {
dateFormats[tense].forEach(format => {
if (format.ceiling == null || secondsDifference <= format.ceiling) {
return format;
}
});
return null;
}
function getTimeBreakdown() {
let seconds = secondsDifference;
const breakdown = {};
timeUnits.forEach(timeUnit => {
const occurencesOfUnit = Math.floor(seconds / timeUnit[0]);
seconds -= timeUnit[0] * occurencesOfUnit;
breakdown[timeUnit[1]] = occurencesOfUnit;
});
return breakdown;
}
function depluralizeTimeAgoText(timeAgoText, breakdown) {
let cleanedTimeAgoText = '';
breakdown.forEach((bd, index) => {
if (bd === 1) {
const regexp = new RegExp(`\\b${index}\\b`);
cleanedTimeAgoText = timeAgoText.replace(regexp, text =>
text.replace(/s\b/g, ''),
);
}
});
return cleanedTimeAgoText;
}
function renderDate(dateFormat) {
const breakdown = getTimeBreakdown();
const timeAgoText = dateFormat.text.replace(
/\$(\w+)/g,
(_match, p1) => breakdown[p1],
);
return depluralizeTimeAgoText(timeAgoText, breakdown);
}
return renderDate(getFormat());
};
export const oneWeek = 60 * 60 * 24 * 7;
export const formatDate = (date, options) => {
if (!date) {
return null;
}
return new Intl.DateTimeFormat('default', options).format(new Date(date));
};
const threeDays = 60 * 60 * 24 * 3;
// Show human time (8 minutes ago, 2 hours ago, etc) if less than X days.
export const humanReadableDate = (
currentDate,
refDate,
xDaysInSeconds,
options,
) =>
(currentDate - new Date(refDate)) / 1000 > (xDaysInSeconds || threeDays)
? formatDate(refDate, options || dateOptions)
: timeAgo(refDate);
export const longHumanReadableDate = refDate =>
refDate &&
humanReadableDate(new Date(), refDate, oneWeek, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment