最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin22浏览0评论

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
  • 1 it seems to me like you could use differenceInMinutes then achieve the rest yourself by dividing by minutes in a year, etcetera. – Jeremy Kahan Commented May 15, 2019 at 12:13
  • There's an open issue in their tracker. This feature is not yet implemented, but you can work it out yourself as Jeremy described. This library has functions like 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 to distanceInWords which would allow to specify the output format. – Tomasz Kasperczyk Commented May 15, 2019 at 12:25
  • @Tom, Would you be ok if the solution was achieved using plain JS? And no library? – RichS Commented May 16, 2019 at 4:49
Add a comment  | 

3 Answers 3

Reset to default 25

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

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

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论