I'm using DateFNS and I need to generate a countdown with it. distanceInWordsToNow
only outputs in about 3 years but I need the exact time like 3 Years, 11 Months, 20 Days, 3 Hours, 2 Minutes
. How to archive that with DateFNS?
Here is a CodePen example:
SCRIPT
todaysDateMin: dateFns.distanceInWordsToNow(new Date(2022, 6, 2, 0, 0, 15), {addSuffix: true})
I'm using DateFNS and I need to generate a countdown with it. distanceInWordsToNow
only outputs in about 3 years but I need the exact time like 3 Years, 11 Months, 20 Days, 3 Hours, 2 Minutes
. How to archive that with DateFNS?
Here is a CodePen example: https://codepen.io/anon/pen/qGajJB
SCRIPT
todaysDateMin: dateFns.distanceInWordsToNow(new Date(2022, 6, 2, 0, 0, 15), {addSuffix: true})
Share
Improve this question
edited May 16, 2019 at 7:19
Akrion
18.5k1 gold badge36 silver badges57 bronze badges
asked May 13, 2019 at 11:17
TomTom
5,97421 gold badges85 silver badges134 bronze badges
3
|
3 Answers
Reset to default 25The formatDuration()
function in date-fns v2 does exactly what you want it to.
import { formatDuration, intervalToDuration } from 'date-fns'
let duration = intervalToDuration({
start: new Date(2022, 6, 2, 0, 0, 15),
end: new Date(),
})
formatDuration(duration, {
delimiter: ', '
})
// 2 years, 15 days, 23 hours, 49 minutes, 35 seconds
You can even filter it.
// take the first three nonzero units
const units = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']
const nonzero = Object.entries(duration).filter(([_, value]) => value || 0 > 0).map(([unit, _]) => unit)
formatDuration(duration, {
format: units.filter(i => new Set(nonzero).has(i)).slice(0, 3),
delimiter: ', '
})
// 2 years, 15 days, 23 hours
You can try something like this:
let humanizeFutureToNow = fDate => {
let result = [], now = new Date()
let parts = ['year', 'month', 'day', 'hour', 'minute']
parts.forEach((p, i) => {
let uP = p.charAt(0).toUpperCase() + p.slice(1)
let t = dateFns[`differenceIn${uP}s`](fDate, now);
if (t) {
result.push(`${i===parts.length-1 ? 'and ' : ''}${t} ${uP}${t===1 ? '' : 's'}`);
if (i < parts.length)
fDate = dateFns[`sub${uP}s`](fDate, t);
}
})
return result.join(' ');
}
console.log(humanizeFutureToNow(new Date('2022-11-11')))
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js"></script>
The idea is to run through all the time periods you want (and have supported date-fns functions) and generate an array with all the strings. We do this by running the supported differenceIn<PARTGOESHERE>
first to get the time distance
and then subtract it using the sub<PARTGOESHERE>
function. After that just join them to compose the final string.
From here you can customize and export the parts as a parameter, as well as the uppercasing of the first letters in the output etc etc.
Here is also a lodash version:
let humanizeFutureToNow = fDate => {
let result = [], now = new Date()
let parts = ['year', 'month', 'day', 'hour', 'minute']
_.each(parts, (p, i) => {
let scPart = _.startCase(p)
let t = _.invoke(dateFns, `differenceIn${scPart}s`, fDate, now);
if (t) {
result.push(`${i===parts.length-1 ? 'and ' : ''}${t} ${scPart}${t===1 ? '' : 's'}`);
if (i < parts.length)
fDate = _.invoke(dateFns, `sub${scPart}s`, fDate, t);
}
})
return result.join(' ');
}
console.log(humanizeFutureToNow(new Date('2022-11-11')))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js"></script>
Use the following dateFns functions to get the components, then concatenate them together with the strings.
let x be the smaller year, y be the larger save differenceInYears called on x and y which gives the whole number of years in a variable pass x and that as a parameter to addYears, assign to x
call differenceInMonths on x and y, save call addMonths on x and that saved number of months
do the same with differenceInDays and addDays, differenceInHours and addHours Now x is less than 60 minutes away from y, so call differenceInMinutes and you're done (assuming your countDown is down to the minute).
Here is your example as run on runkit.com to illustrate the method.
var dateFns = require("date-fns");
var x = new Date();
var y = new Date(2022, 2, 6, 0, 0, 15);
var temp;
temp = dateFns.differenceInYears(y, x);
var result = temp + " years ";
x = dateFns.addYears(x, temp);
temp = dateFns.differenceInMonths(y, x);
result = result + temp + " months ";
x = dateFns.addMonths(x, temp);
temp = dateFns.differenceInDays(y, x);
result = result + temp + " days ";
x = dateFns.addDays(x, temp);
temp = dateFns.differenceInHours(y, x);
result = result + temp + " hours ";
x = dateFns.addHours(x, temp);
temp = dateFns.differenceInMinutes(y, x);
result = result + temp + " minutes ";
x = dateFns.addMinutes(x, temp);
temp = dateFns.differenceInSeconds(y, x);
result = result + temp + " seconds";
console.log(result);
differenceInYears
,differenceInMonths
,differenceInDays
etc. All you need to do is to build the final string by using them. In the future, they're planning to introduce an additional parameter todistanceInWords
which would allow to specify the output format. – Tomasz Kasperczyk Commented May 15, 2019 at 12:25