Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Countdown in date-fns with years, months, days, hours & minutes

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})
like image 324
Tom Avatar asked May 13 '19 11:05

Tom


People also ask

Does date-fns support timezone?

date-fns respects timezones & DST. It follows semantic versioning so, always backward compatible. Each build CI checks more than 650 000 examples in about 400 time zones. The best API is an API that doesn't exist.

Is moment or date-fns better?

Date-fns simplifies the JavaScript data manipulations by providing a variety of functions. It provides more than 200 different time-related functions, and unlike MomentJS, it allows you to import a selected set of functions as needed.

How can I get current date and time in FNS?

//import the function you want to use const {format} = require('date-fns'); const {format} = require('date-fns'); //today's date const today =format(new Date(),'dd. MM. yyyy'); console.

What is date-fns in react?

date-fns provides the most comprehensive, yet simple and consistent toolset. for manipulating JavaScript dates in a browser & Node. js.


3 Answers

The 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
like image 121
I'll Eat My Hat Avatar answered Oct 21 '22 10:10

I'll Eat My Hat


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>
like image 29
Akrion Avatar answered Oct 21 '22 09:10

Akrion


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);
like image 42
Jeremy Kahan Avatar answered Oct 21 '22 10:10

Jeremy Kahan