Search code examples
javascriptdatecountdowndate-fns

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})

Solution

  • 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>