Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: A more accurate .humanize #348

Closed
FGRibreau opened this issue Jun 24, 2012 · 117 comments
Closed

Feature request: A more accurate .humanize #348

FGRibreau opened this issue Jun 24, 2012 · 117 comments

Comments

@FGRibreau
Copy link

.humanize isn't really accurate when it comes to seconds.

moment.duration(40, 's').humanize() === "a few seconds" // expected "40 seconds"
moment.duration(45, 's').humanize() === "a minute" // expected "45 seconds"

I understand that we don't often want this level of accuracy but sometimes we do want the exact value in a human readable way.

I suggest a change to the API (which seems backwards compatible from what I actually see) to support this feature:

moment.duration(40, 's').humanize() === "a few seconds"
moment.duration(40, 's').humanize(true) === "in a few seconds"

moment.duration(40, 's').humanize({precise:true}) === "40 seconds"

moment.duration(40, 's').humanize({suffix:true, precise:true}) === "in 40 seconds"
moment.duration(40, 's').humanize({precise:false, suffix:true}) === "in a few seconds"

What do you think ?

@ghost ghost assigned rockymeza Jun 24, 2012
@rockymeza
Copy link
Contributor

@FGRibreau, this issue came up in #232, and the OP over there seemed to be OK not adding that to core.

You could write a language definition called precise-english or something for when you want to more precise relative times. With the new instance lang feature coming out in 1.7.0, it could be pretty useful. Right now any language definition will inherit all non-specified values from English, so you would just have to change the relativeTime dictionary. Unfortunately, the inheritance doesn't inherit within dictionaries, so you have to specify the whole dictionary.

Please note: the method described in #232 will not work anymore in 1.7.0, because of the changes in #332.

moment.lang('precise-en', {
    relativeTime : {
        future : "in %s",
        past : "%s ago",
        s : "%d seconds", //see https://github.com/timrwood/moment/pull/232#issuecomment-4699806
        m : "a minute",
        mm : "%d minutes",
        h : "an hour",
        hh : "%d hours",
        d : "a day",
        dd : "%d days",
        M : "a month",
        MM : "%d months",
        y : "a year",
        yy : "%d years"
    }
});

// one way
moment.lang('precise-en');
moment.duration({s: 40}).humanize();

// other way
moment.duration({s: 40}).lang('precise-en').humanize();

One thing that would make this easier would be for the inheritance to work so that you only have to specify just one value inside of the relativeTime object. @timrwood, do you think we need smarter inheritance?

@timrwood
Copy link
Member

timrwood commented Jul 5, 2012

If we add in smarter inheritance, I would want to possibly do something like CLDR does and have each sub-language inherit from the master language. So fr_CA would inherit from fr and en_GB would inherit from en.

I don't think we need to do deep extending to change out only 'relativeTime.s' though.

@rockymeza
Copy link
Contributor

@FGRibreau did something along the lines of a precise-en language definition work for you?

@timrwood
Copy link
Member

Closing this as @rockymeza's solution is the preferred way of handling this.

@sneakyness
Copy link

This applies to more than just seconds. The same applies to hours, years, etc...

@inca
Copy link

inca commented Aug 18, 2013

Yeap, and the preferred solution is preferred only for english-speaking auditory. In other langs you have to bother yourself with noun inflections (declensions).

@Offirmo
Copy link

Offirmo commented Sep 27, 2013

I add weight for this issue. When devising a countdown, the precision is needed.

So it's OK to have "10 hours ago" when looking at past things, but if eagerly waiting for an event, I want "in 10 hours 3 minutes 8 seconds"

Don't tell me that I have to write a custom language to obtain that ?

@igorescobar
Copy link

moment.duration(183, "minutes").humanize() // 3 hours

Would be nice to have de precise output like: 3 hours and 3 seconds.

@Envek
Copy link

Envek commented Aug 27, 2014

I want this feature too.

@miguelcobain
Copy link

I also need a precise humanize version!

@doingweb
Copy link

doingweb commented Jan 8, 2015

I also think it would be nice for Moment's humanize to be (optionally) more precise, but for anyone who came here looking for a solution right now, I've found that HumanizeDuration.js works pretty well:

var
  moment = require('moment'),
  humanizeDuration = require('humanize-duration');

console.log(humanizeDuration(moment.duration('PT1H15M').asMilliseconds())); // '1 hour, 15 minutes'

// whereas:

console.log(moment.duration('PT1H15M').humanize()); // 'an hour'

@bborowin
Copy link

Would love to see an "accurate humanize" option, having to use an additional library on top of moment is a little clunky..

@Jamie452
Copy link

Jamie452 commented Jun 9, 2015

+1 for precise humanize!

@iambibhas
Copy link

Really disappointed to see that there is no option to get the precise humanized duration. :|

@Envek
Copy link

Envek commented Jun 11, 2015

There is some plugin called Precise Range referenced in the docs. Have anyone tried it?

@marseille
Copy link

Precise Range technically works, but it seems to return a string instead of a moment object, so I can't pick out specifically what date parts I might like.

The language (imo) is a bit off too, one result was - 'an hour 19 minutes a few seconds' . I'd prefer to see something like 1 hour 19 minutes and a few seconds. Mixing words and numbers has always been a bit of a grammatical no-no (as far as I know).

+1 for precise moment humanize

@topaxi
Copy link
Contributor

topaxi commented Aug 11, 2015

@Envek Precise Range seems nice, doesn't support any i18n/l10n though...

Official support by moment.js would be nice as I needed this in at least two or three of my projects.

@patgod85
Copy link

@topaxi +1

@nikulinsanya
Copy link

+1

@Offirmo
Copy link

Offirmo commented Feb 11, 2016

@doingweb I confirm that the HumanizeDuration.js module works pretty well !

@pboulet
Copy link

pboulet commented Feb 13, 2016

+1 for precise moment humanize

@ismarslomic
Copy link

+1 for more precise moment humanize

@Dbuggerx
Copy link

+1

2 similar comments
@Aaronmsv
Copy link

+1

@MichaelTSS
Copy link

+1

@LazyCompiler
Copy link

+1 😞
Other libraries does not support such a big variety of languages like Moment.js does. Please reconsider!

@xinthose
Copy link

+1

2 similar comments
@nixxholas
Copy link

+1

@morfair
Copy link

morfair commented Jun 11, 2019

+1

@stychu
Copy link

stychu commented Jun 12, 2019

+1

P.S. unbelievable it's not yet implemented ;(

@Tyguy7
Copy link

Tyguy7 commented Jun 28, 2019

+1 really, this still isn't here?!

@inca
Copy link

inca commented Jun 28, 2019

I'm finally unsubscribing (it's been roughly 7 years already, but I think I finally had it with all those +1s)

@schnetzi
Copy link

I found a package which solves my problems 🎉
https://github.com/EvanHahn/HumanizeDuration.js

Maybe it is useful for anyone else as well.

@pavelzubov
Copy link

+1

3 similar comments
@denmasyarikin
Copy link

+1

@dmacompton
Copy link

+1

@cassiebrooks
Copy link

+1

@koenig-dominik
Copy link

koenig-dominik commented Oct 17, 2019

My take on that problem:

const units: Array<{unit: moment.unitOfTime.Base, key: moment.RelativeTimeKey}> = [
  {unit: 'y', key: 'yy'},
  {unit: 'M', key: 'MM'},
  {unit: 'd', key: 'dd'},
  {unit: 'h', key: 'hh'},
  {unit: 'm', key: 'mm'},
  {unit: 's', key: 'ss'},
];
function accurateHumanize(duration: moment.Duration, accuracy: number = 2): string {
  let beginFilter = false;
  let componentCount = 0;

  return units
    .map(({unit, key}) => ({value: duration.get(unit), key}))
    .filter(({value, key}) => {
      if (beginFilter === false) {
        if (value === 0) {
          return false;
        }
        beginFilter = true;
      }
      componentCount++;
      return value !== 0 && componentCount <= accuracy;
    })
    .map(({value, key}) => ({value: value, key: value === 1 ? key[0] as moment.RelativeTimeKey : key}))
    .map(({value, key}) => moment.localeData().relativeTime(value, true, key, true))
    .join(', ');
}

@mirague
Copy link

mirague commented Nov 13, 2019

This makes a lot of sense, would love to see a precise formatting of a duration.

@iShotFT
Copy link

iShotFT commented Apr 10, 2020

Would love the option to humanize without rounding
+1

@HendrikPetertje
Copy link

+1 on this

@yojona
Copy link

yojona commented Apr 17, 2020

+1

3 similar comments
@alzaabi98
Copy link

+1

@nathan818fr
Copy link

+1

@hossam-magdy
Copy link

+1

@hossam-magdy
Copy link

hossam-magdy commented May 17, 2020

Just in case anyone lands here after googling, this config/workaround was sufficient in my case:

moment.relativeTimeRounding((t) => {
  const DIGITS = 2; // like: 2.56 minutes
  return Math.round(t * Math.pow(10, DIGITS)) / Math.pow(10, DIGITS);
});
moment.relativeTimeThreshold('y', 365);
moment.relativeTimeThreshold('M', 12);
moment.relativeTimeThreshold('w', 4);
moment.relativeTimeThreshold('d', 31);
moment.relativeTimeThreshold('h', 24);
moment.relativeTimeThreshold('m', 60);
moment.relativeTimeThreshold('s', 60);
moment.relativeTimeThreshold('ss', 0);

console.log(moment.duration(89, 's'));
// Output: "1.48 minutes"
// … without configuring the moment.relative*(), output was: "a minute"

because accuracy in my particular use case is much more important than a nice looking text.

And this is a comparison before and after configuring/invoking moment.relative*() methods:

duration before after
3s a few seconds 3 seconds
44s a few seconds 44 seconds
45s a minute 45 seconds
1m 29s a minute 1.48 minutes
1m 30s 2 minutes 1.5 minutes
1m 59s 2 minutes 1.98 minutes
44m 44 minutes 44 minutes
45m an hour 45 minutes
1h 29m an hour 1.48 hours
1h 30m 2 hours 1.5 hours
21h 21 hours 21 hours
22h a day 22 hours
35h a day 35 hours
35h 30m a day 35.5 hours
36h 2 days 36 hours

jpqy added a commit to Folkwise-io/MintbeanPlatformV3-frontend that referenced this issue Sep 24, 2020
momentjs's `.humanize` sadly does not allow for specifying the
precision you want (see moment/moment#348), so all the 4.5 hour
hackathons were displaying as 4. The humanize-duration library allows
us to specify the precision and our durations are now more readable.
@Gredys
Copy link

Gredys commented Aug 4, 2021

Holy cow... That's still not fixed!? ._.

@ghost
Copy link

ghost commented Aug 4, 2021

@Gredys - and it won't be! As per the README for "moment":

Moment.js is a legacy project, now in maintenance mode. In most cases, you should choose a different library.

I now use dayjs + https://www.npmjs.com/package/humanize-duration which works well for me

@Gredys
Copy link

Gredys commented Sep 3, 2021

@digitaltoast thats the reasion why I am not going to use anymore in my projects. (yeah, using same bundles as u)

@divyanshrws
Copy link

This should be included in the moment library instead of giving it as a separate plugin

@satarovbekzhan
Copy link

Let our grandsons do the implement this feature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests