Full humanized durations in moment.js

Momentjs

Momentjs Problem Overview


I tried this in moment.js

moment.duration(375,'days').humanize()

and get "a year" as answer, but I would expect "a year and 10 days". Is there a way in moment.js to get the full humanized value?

Momentjs Solutions


Solution 1 - Momentjs

Moment.js is providing the fromNow function to get time durations in human readable fromat, see http://momentjs.com/docs/#/displaying/fromnow/

Example:

moment([2007, 0, 29]).fromNow(); // 4 years ago
moment().subtract(375, 'days').fromNow(); // a year ago

You need to use third party lib as suggested by @Fluffy

Solution 2 - Momentjs

I found this small lib, that only display duration (if you don't really need all the features of moment.js)

https://github.com/EvanHahn/HumanizeDuration.js

Solution 3 - Momentjs

Try this plugin:

https://github.com/jsmreese/moment-duration-format

moment.duration(123, "minutes").format("h [hrs], m [min]");
// "2 hrs, 3 min"

Solution 4 - Momentjs

I was looking at the same issue and seems like there is no plan on supporting this in the future...

Although one workaround proposed is to make an language definition that overrides default implementation of humanized messages:

https://github.com/timrwood/moment/issues/348

Kind of an overkill if you ask me...

Solution 5 - Momentjs

Use moment.relativeTimeThreshold('y', 365) to set the rounding.

moment.relativeTimeThreshold('s', 60);
moment.relativeTimeThreshold('m', 60);
moment.relativeTimeThreshold('h', 24);
moment.relativeTimeThreshold('d', 31);
moment.relativeTimeThreshold('M', 12);
moment.relativeTimeThreshold('y', 365);

Solution 6 - Momentjs

I made a function to solve this exact problem.

function formatDuration(period) {
    let parts = [];
    const duration = moment.duration(period);
    
    // return nothing when the duration is falsy or not correctly parsed (P0D)
    if(!duration || duration.toISOString() === "P0D") return;
		
    if(duration.years() >= 1) {
      	const years = Math.floor(duration.years());
      	parts.push(years+" "+(years > 1 ? "years" : "year"));
    }
    
    if(duration.months() >= 1) {
    	const months = Math.floor(duration.months());
    	parts.push(months+" "+(months > 1 ? "months" : "month"));
    }
    
    if(duration.days() >= 1) {
    	const days = Math.floor(duration.days());
      	parts.push(days+" "+(days > 1 ? "days" : "day"));
    }
    
    if(duration.hours() >= 1) {
    	const hours = Math.floor(duration.hours());
      	parts.push(hours+" "+(hours > 1 ? "hours" : "hour"));
    }
    
    if(duration.minutes() >= 1) {
      	const minutes = Math.floor(duration.minutes());
      	parts.push(minutes+" "+(minutes > 1 ? "minutes" : "minute"));
	}
    
    if(duration.seconds() >= 1) {
    	const seconds = Math.floor(duration.seconds());
      	parts.push(seconds+" "+(seconds > 1 ? "seconds" : "second"));
    }
    
    return "in "+parts.join(", ");
}

This function takes a period string (ISO 8601), parses it with Moment (>2.3.0) and then, for every unit of time, pushes a string in the parts array. Then everything inside the parts array gets joined together with ", " as separation string.

You can test it here: https://jsfiddle.net/mvcha2xp/6/

I'm using it as a Vue filter to humanize durations correctly.

Solution 7 - Momentjs

This issue on Github contains a lot of discussion about exactly that. Many are asking for a more precise humanized option.

Chime in with why you need it, use cases, etc.

https://github.com/moment/moment/issues/348

Solution 8 - Momentjs

i have written this javascript code to humanize the duration,

function humanizeDuration(timeInMillisecond) {
    var result = "";
    if (timeInMillisecond) {
        if ((result = Math.round(timeInMillisecond / (1000 * 60 * 60 * 24 * 30 * 12))) > 0) {//year
            result = result === 1 ? result + " Year" : result + " Years";
        } else if ((result = Math.round(timeInMillisecond / (1000 * 60 * 60 * 24 * 30))) > 0) {//months
            result = result === 1 ? result + " Month" : result + " Months";
        } else if ((result = Math.round(timeInMillisecond / (1000 * 60 * 60 * 24))) > 0) {//days
            result = result === 1 ? result + " Day" : result + " Days";
        } else if ((result = Math.round(timeInMillisecond / (1000 * 60 * 60))) > 0) {//Hours
            result = result === 1 ? result + " Hours" : result + " Hours";
        } else if ((result = Math.round(timeInMillisecond / (1000 * 60))) > 0) {//minute
            result = result === 1 ? result + " Minute" : result + " Minutes";
        } else if ((result = Math.round(timeInMillisecond / 1000)) > 0) {//second
            result = result === 1 ? result + " Second" : result + " Seconds";
        } else {
            result = timeInMillisecond + " Millisec";
        }
    }
    return result;
}

Solution 9 - Momentjs

One of the solutions:

function getCountdown() {
  // diff in seconds, comes through function's params
  const diff = 60*60*24*4 + 60*60*22 + 60*35 + 5;
  const MINUTE = 60;
  const HOUR = MINUTE * 60;
  const DAY = HOUR * 24;

  const days = Math.floor(diff / DAY);
  const hDiff = diff % DAY;
  const hours = Math.floor(hDiff / HOUR);
  const mDiff = hDiff % HOUR;
  const minutes = Math.floor(mDiff / MINUTE);
  const seconds = mDiff % MINUTE;

  return [days, hours, minutes, seconds]
    .map(v => (''+v)[1] ? ''+v : '0'+v)
}


output: ["04", "22", "35", "05"]

I needed it up to days only, but can be easily extended to weeks. Doesn't make sense with months since diff says nothing about start date. Having a period split to parts, adding "days"/"hours"/... is obvious.

Solution 10 - Momentjs

Moment.js provides:

var y = moment.duration(375,'days').years(); // returns 1
var d = moment.duration(375,'days').days(); // returns 9

var data = y + 'y ' + d + 'd';

console.log(data);

This could be used with a bit of extra logic

Solution 11 - Momentjs

This is my solution on CoffeeScript:

humanizeDuration = (eventDuration)->
    eventMDuration = Moment.duration(eventDuration, 'seconds');
    eventDurationString = ""
    if (eventMDuration.days() > 0)
        eventDurationString += " " + Moment.duration(eventMDuration.days(), 'days').humanize()
    if (eventMDuration.hours() > 0)
        eventDurationString += " " + Moment.duration(eventMDuration.hours(), 'hours').humanize()
    if (eventMDuration.minutes() > 0)
        eventDurationString += " " + Moment.duration(eventMDuration.minutes(), 'minutes').humanize()

    eventDurationString.trim()

Solution 12 - Momentjs

This is my solution, I like it better than the others here:

val moment1 = moment();
val moment2 = mement();
console.log(moment.duration(moment1.diff(moment2)).humanize());

Solution 13 - Momentjs

Based on Ihor Kaslashnikov's solution, I modified the function to be even more accurate using vanilla Javascript.

function momentHumanize(eventDuration, unit) {
    var eventMDuration = moment.duration(eventDuration, unit);
    var eventDurationArray = [];
    if (eventMDuration.years() > 0) {
        eventDurationArray.push(eventMDuration.years() + ' years');
        eventMDuration.subtract(eventMDuration.years(), 'years')
    }
    if (eventMDuration.months() > 0) {
        eventDurationArray.push(eventMDuration.months() + ' months');
        eventMDuration.subtract(eventMDuration.months(), 'months')
    }
    if (eventMDuration.weeks() > 0) {
        eventDurationArray.push(eventMDuration.weeks() + ' weeks');
        eventMDuration.subtract(eventMDuration.weeks(), 'weeks')
    }
    if (eventMDuration.days() > 0) {
        eventDurationArray.push(eventMDuration.days() + ' days');
        eventMDuration.subtract(eventMDuration.days(), 'days')
    }
    if (eventMDuration.hours() > 0) {
        eventDurationArray.push(eventMDuration.hours() + ' hours');
        eventMDuration.subtract(eventMDuration.hours(), 'hours')
    }
    if (eventMDuration.minutes() > 0) {
        eventDurationArray.push(eventMDuration.minutes() + ' minutes');
    }
    return eventDurationArray.length === 1 ? eventDurationArray[0] : 
    eventDurationArray.join(' and ')
}

This will remove any amount from the moment instance once it humanizes it. I did this because Ihor's solution was inaccurate, given that moment's humanize function rounds the value. For example, if I had 2.8 hours, it should've been 2 hours and an hour. My solution removes the 2 hours, from the instance, leaving only 0.8 hours, and doesn't use moment's humanize function to avoid rounding.

Examples:

momentHumanize(45, 'minutes') // 45 minutes

momentHumanize(4514, 'minutes') // 3 days and 3 hours and 14 minutes

momentHumanize(45145587, 'minutes') // 85 years and 10 months and 1 days and 2 hours and 27 minutes

Solution 14 - Momentjs

var s=moment([2020, 03, 29]).subtract(3, 'days').fromNow(); 

document.write(s)

enter link description here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhusaytView Question on Stackoverflow
Solution 1 - MomentjsKishore RelangiView Answer on Stackoverflow
Solution 2 - MomentjsNicolas CView Answer on Stackoverflow
Solution 3 - MomentjsFluffyView Answer on Stackoverflow
Solution 4 - MomentjspflopezView Answer on Stackoverflow
Solution 5 - MomentjsRJFalconerView Answer on Stackoverflow
Solution 6 - MomentjsSyncroITView Answer on Stackoverflow
Solution 7 - MomentjsJoshua DanceView Answer on Stackoverflow
Solution 8 - Momentjsshyam_View Answer on Stackoverflow
Solution 9 - Momentjsgeorgiy.zhuravlevView Answer on Stackoverflow
Solution 10 - MomentjsJarkko OksanenView Answer on Stackoverflow
Solution 11 - MomentjsIgor KalashnikovView Answer on Stackoverflow
Solution 12 - MomentjsjhildenView Answer on Stackoverflow
Solution 13 - MomentjsTarek DeebView Answer on Stackoverflow
Solution 14 - MomentjsNishithView Answer on Stackoverflow