Moment: 功能要求:更准确的 .humanize

创建于 2012-06-24  ·  112评论  ·  资料来源: moment/moment

.humanize 在秒数方面并不是很准确。

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

我知道我们通常不想要这种级别的准确性,但有时我们确实希望以人类可读的方式获得确切的值。

我建议更改 API(从我实际看到的来看似乎向后兼容)以支持此功能:

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"

你怎么看 ?

Enhancement New Feature

最有用的评论

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

如果有精确的输出,比如:3 小时 3 秒,那就太好了。

所有112条评论

@FGRibreau ,这个问题出现在 #232 中,那里的 OP 似乎可以不将它添加到核心。

当您想要更精确的相对时间时,您可以编写一个名为precise-english的语言定义或其他东西。 随着新的实例语言特性在 1.7.0 中出现,它可能非常有用。 现在任何语言定义都将从英语继承所有未指定的值,因此您只需要更改 relativeTime 字典。 不幸的是,继承不会在字典中继承,因此您必须指定整个字典。

请注意: #232 中描述的方法在 1.7.0 中将不再适用,因为 #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();

使这更容易的一件事是继承工作,因此您只需在relativeTime对象内指定一个值。 @timrwood ,你认为我们需要_smarter_继承吗?

如果我们添加更智能的继承,我可能想做一些像 CLDR 那样的事情,并让每个子语言都从主语言继承。 所以fr_CA将继承自fr并且en_GB将继承自en

我认为我们不需要进行深度扩展来仅更改“relativeTime.s”。

@FGRibreau为您做了类似于precise-en语言定义的工作?

将其作为@rockymeza的解决方案关闭是处理此问题的首选方式。

这不仅仅适用于几秒钟。 这同样适用于小时、年等......

是的,首选解决方案仅适用于讲英语的听觉。 在其他语言中,您必须用名词变形(变格)来打扰自己。

我为这个问题增加了权重。 在设计倒计时时,需要精确度。

所以看过去的事情时有“10小时前”是可以的,但是如果急切地等待一个事件,我想要“10小时3分8秒”

不要告诉我我必须编写自定义语言才能获得它?

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

如果有精确的输出,比如:3 小时 3 秒,那就太好了。

我也想要这个功能。

我还需要一个精确的人性化版本!

我也认为 Moment 的人性化(可选)更精确会很好,但对于现在来这里寻找解决方案的任何人,我发现HumanizeDuration.js工作得很好:

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'

希望看到一个“准确的人性化”选项,不得不在时刻使用额外的库有点笨拙..

+1 精确人性化!

看到无法获得精确的人性化持续时间,真的很失望。 :|

文档中引用了一些名为Precise Range的插件。 有人试过吗?

精确范围在技术上有效,但它似乎返回一个字符串而不是一个时刻对象,所以我无法具体挑选出我可能喜欢的日期部分。

语言(imo)也有点偏离,一个结果是 - 'an hour 19 minutes a few seconds' 。 我希望看到类似 1 小时 19 分零几秒的内容。 混合单词和数字一直是语法上的禁忌(据我所知)。

+1 精确时刻人性化

@Envek Precise Range 看起来不错,虽然不支持任何 i18n/l10n...

moment.js 的官方支持会很好,因为我至少有两三个项目需要这个。

@topaxi +1

+1

@doingweb我确认 HumanizeDuration.js 模块运行良好!

+1 精确时刻人性化

+1 更精确的时刻人性化

+1

+1

+1

+1

+1

+1

+1

为什么这个一直关门? 这是必须在 moment.js 中实现的最明显的功能。 我真的很震惊,它还没有出现。 Humanize 在任何地方都毫无用处,除了一些模糊的时间显示,比如在没有它的论坛上的帖子中。

+1

+1

+1

-1。 我喜欢人性化它现在的工作方式。 它应该给出持续时间的“人类友好”版本。

“嘿弗兰克,你什么时候发的那封邮件?”
“大约一分钟前。”

对比

“嘿弗兰克,你什么时候发的那封邮件?”
“四十三秒前。”

哪个看起来更“人性化”?

更准确的版本不应该取代当前版本。 这是对新功能的要求。

+1, @mattgrande虽然我们现在拥有的内容非常适合描述过去某事的持续时间,但它不适合描述,例如约会的长度,例如“1 小时 30 分钟”。

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

为此我+1。 制定新的语言定义,或修改现有的定义,并没有真正奏效。 看起来 Moment 目前支持 108 种语言,所以我们要么必须手动修改 108 种定义(并希望我们让它们在语法上都正确),还是忘记任何不会说英语的用户? 这些听起来都不是一个好的解决方案......

我正在使用这个:

````javascript
var humanizeDuration = 函数(eventDuration){
eventMDuration = moment.duration(eventDuration, 'seconds');
事件持续时间字符串 = ""
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()
}
````

+1

+1 如果没有此功能,则无法进行时间跟踪。

为什么关闭问题?

+1

+1

+1

有选择地添加更精确但仍然是“人类”选项的能力将非常有价值。

+1

+1

+1

+1

+1。 处理需要准确显示持续时间但以人类格式显示的会计应用程序。 “1年2个月3天”比“398天”要好,但是“1年”是不能接受的。

当前的实现是如此人性化,以至于基本上没有用。

+1

duration.humanize具有定义何时将单位视为一分钟、一小时等的阈值。 例如,默认情况下,超过 45 秒被认为是一分钟,超过 22 小时被认为是一天等等。 要更改这些截止值,请使用moment.relativeTimeThreshold(unit, limit)其中单位是sssmhd之一, M

https://momentjs.com/docs/#/customization/relative -time-threshold/

+1

+1

+1,请重新考虑打开这个问题😍

+1

+1

+1

+1

+1

+1

我使用这样的东西:

function stringifyDuration(duration) {
    // Return a human readable string representing the given moment duration

    // Split the duration into parts, and drop the most significant parts with a value of 0
    const durationParts = _.dropWhile([
        { value: duration.years(), unit: 'year' },
        { value: duration.months(), unit: 'month' },
        { value: duration.days(), unit: 'day' },
        { value: duration.hours(), unit: 'hour' },
        { value: duration.minutes(), unit: 'minute' },
        { value: duration.seconds(), unit: 'second' },
        { value: duration.milliseconds(), unit: 'millisecond' },
    ], (part) => part.value === 0);

    // Display up to two of the most significant remaining parts
    return _.take(durationParts, 2).map((part) => (
        `${part.value} ${pluralize(part.unit, part.value)}`
    )).join(', ');
}
> stringifyDuration(moment.duration(26, 'hours'))
"1 day, 2 hours"

使用lodash复数

@timrwood一个实现这个的案例:

  1. @rockymezahttps://github.com/moment/moment/issues/348#issuecomment -6535794 解决方案意味着我们(那里的每个开发人员)必须用他们支持的每种语言重新实现它,即使您和您的贡献者已经完成了翻译成多种语言的惊人工作
  2. @mattgrande https://github.com/moment/moment/issues/348#issuecomment -237830415 认为它已经足够人性化的论点只是在呼唤那种效果很好的部分。 考虑一下这段对话:

嘿弗兰克,我要去吃午饭,距离下次会议还有多长时间?
一小时

Now, let's see what that "hour" can mean in `moment`:

```js
moment.duration({hours: 0, minutes: 45}).humanize() === "an hour"
moment.duration({hours: 1, minutes: 29}).humanize() === "an hour"
```
This means that if we understand things differently I could be ~45 minutes early or late for that meeting.
  1. 感谢@sproot调用 RTFM

    moment.relativeTimeThreshold('m', 60);
    moment.duration({hours: 0, minutes: 45}).humanize() === "45 minutes"
    moment.duration({hours: 0, minutes: 59}).humanize() === "59 minutes"
    moment.duration({hours: 0, minutes: 60}).humanize() === "an hour"
    moment.duration({hours: 1, minutes: 29}).humanize() === "an hour"
    

    稍微好一点,但当休息时间超过 60 分钟时仍然存在问题。 大约 30 分钟的差异可能意味着我可以回家,吃饭然后回来,或者我必须在附近拿点东西。

  2. @Nerian https://github.com/moment/moment/issues/348#issuecomment -279819773 和@cogwirrel https://github.com/moment/moment/issues/348#issuecomment -346233713 结合@sproot的解决方法s 提示无需额外的库即可获得“足够好”的实现(_ 部分可以使用普通 JS 实现),但它可以直接支持一些东西:

    var el = document.createElement('script');
    el.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js";
    document.head.appendChild(el)
    moment.relativeTimeThreshold('s', 60);
    moment.relativeTimeThreshold('ss', 0); // must be after 's', disables "few seconds"
    moment.relativeTimeThreshold('m', 60);
    moment.relativeTimeThreshold('h', 24);
    moment.relativeTimeThreshold('d', 31);
    moment.relativeTimeThreshold('M', 12);
    /**
     * Return a precize human readable string representing the given moment duration.
     *
     * <strong i="34">@param</strong> {Moment.Duration} duration
     * <strong i="35">@param</strong> {{mostPreciseUnit: string, numberOfSignificantParts: integer}} options
     */
    moment.duration.fn.humanizePrecisely = function(options = {}) {
        // Split the duration into parts to be able to filter out unwanted ones
        const allParts = [
            { value: this.years(), unit: 'years' },
            { value: this.months(), unit: 'months' },
            { value: this.days(), unit: 'days' },
            { value: this.hours(), unit: 'hours' },
            { value: this.minutes(), unit: 'minutes' },
            { value: this.seconds(), unit: 'seconds' },
            // cannot format with moment.humanize()
            //{ value: duration.milliseconds(), unit: 'milliseconds' },
        ];
    
        return _(allParts)
            // only use the first parts until the most precise unit wanted
            .take(_.findIndex(allParts, {unit: options.mostPreciseUnit || 'seconds'}) + 1)
            // drop the most significant parts with a value of 0
            .dropWhile((part) => part.value === 0)
            // skip other zeroes in the middle (moment.humanize() can't format them)
            .reject((part) => part.value === 0)
            // use only the significant parts requested
            .take(options.numberOfSignificantParts || allParts.length)
            // format each part
            .map((part) => moment.duration(part.value, part.unit).locale(this.locale()).humanize())
            .join(' ');
    }
    moment.duration({hours: 2, minutes: 3, seconds: 4}).locale('de').humanizePrecisely()
        // === "2 Stunden 3 Minuten 4 seconds"
    

    (注意:秒格式和语言环境有问题,将由 #4183 中的 #3981 修复。)

对于它的价值,我刚刚从官方文档中找到了这个:

https://momentjs.com/docs/#/plugins/preciserange/

我自己的代码看起来像这样——而且效果很好:

    var d1 = new Date()
    var d2 = new Date(durationInMinutes * 60 * 1000)
    return moment.preciseDiff(d1 - d2)

我想这有效地解决了这个问题......?

@mercmobily代表 AU、US (CA)、UK:是的,ROW:不是真的; 也许当https://github.com/codebox/moment-precise-range/issues/6是固定的。

(旁注:使用moment*60*1000对我来说看起来有点奇怪,你考虑过moment().add(durationInMinutes, 'minutes')吗?)

啊,Moment我不是很精通! moment().add(durationInMinutes, 'minutes')增加了什么?
我认为一旦该问题关闭,这就是该问题的“解决方案”......

moment()是“现在”(类似于new Date() ),并且add将其增加n分钟。 可以使用任何时刻对象,但不限于“现在”。 (为了让这个已经很长时间的讨论集中起来,让它成为与此边注相关的最后一条评论;)

+1

+2

+1

+1

+1

请重新打开这个。 我的软件想正确显示任何类型的时间间隔,并且将“每 12 秒”更改为“每几秒”或将“每 54 秒”更改为“每分钟”是不可接受的。

+1

+1

我正在做:

moment.duration('PT5S').humanize();

我希望它说:“5 秒”,但它却说“几秒钟”。 我们什么时候可以添加此支持?

+1

+1

这是一个基于@cogwirrel的小型实用程序函数,它不使用 lodash

import { pluralize } from '...'

export function humanize (duration) {
  const durationComponents = [
    { value: duration.years(), unit: 'year' },
    { value: duration.months(), unit: 'month' },
    { value: duration.days(), unit: 'day' },
    { value: duration.hours(), unit: 'hour' },
    { value: duration.minutes(), unit: 'minute' },
    { value: duration.seconds(), unit: 'second' },
    { value: duration.milliseconds(), unit: 'millisecond' }
  ]
  return durationComponents
    .filter(({ value }) => value !== 0)
    .slice(0, 3)
    .map(({ unit, value }) => pluralize(value, unit))
    .join(', ')
}

+1

(我认为,如果您要使用 humanize() 方法,那么将“1.51 小时”转换为“2 小时”并不是特别人性化)。

+1 😞
其他库不支持像 Moment.js 那样种类繁多的语言。 请重新考虑!

+1

+1

+1

+1

PS 难以置信,它还没有实现;(

+1 真的,这还不在这里?!

我终于退订了(已经大约 7 年了,但我想我终于有了所有这些 +1)

我找到了一个可以解决我的问题的包🎉
https://github.com/EvanHahn/HumanizeDuration.js

也许它对其他人也有用。

+1

+1

+1

+1

我对这个问题的看法:

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(', ');
}

这很有意义,希望看到持续时间的精确格式。

希望选择人性化而不四舍五入
+1

对此 +1

+1

+1

+1

+1

以防万一有人在谷歌搜索后登陆这里,在我的情况下,这个配置/解决方法就足够了:

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"

因为在我的特定用例中,准确性比好看的文本重要得多。

Ans 这是配置/调用moment.relative*()方法之前和之后的比较:

| 持续时间 | 之前 | 之后 |
| --------- | --------------- | -------------- |
| 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 |

此页面是否有帮助?
0 / 5 - 0 等级