Last active
November 20, 2019 17:50
-
-
Save obedparla/bc54f59bb325a34edaee4d6ead1604a5 to your computer and use it in GitHub Desktop.
Human readable dates
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // *** 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