Moment: Запрос функции: более точный .humanize

Созданный на 24 июн. 2012  ·  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, и ОП там, похоже, был в порядке, не добавляя это в ядро.

Вы можете написать определение языка под названием precise-english или что-то в этом роде, если вам нужно более точное относительное время. С новой функцией instance lang, появившейся в 1.7.0, это может быть очень полезно. Прямо сейчас любое определение языка будет наследовать все неуказанные значения от английского, поэтому вам просто нужно изменить словарь относительного времени. К сожалению, наследование не распространяется на словари, поэтому вам нужно указать весь словарь.

Обратите внимание: метод, описанный в #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 , как вы думаете, нам нужно более умное наследование?

Если мы добавим более разумное наследование, я бы хотел сделать что-то вроде 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) тоже немного не тот, один результат был - "час 19 минут несколько секунд". Я бы предпочел увидеть что-то вроде 1 часа 19 минут и нескольких секунд. Смешивание слов и чисел всегда было своего рода грамматическим запретом (насколько я знаю).

+1 за конкретный момент гуманизации

@Envek Precise Range кажется хорошим, но не поддерживает i18n / l10n ...

Официальная поддержка со стороны moment.js была бы кстати, так как мне это было нужно по крайней мере в двух или трех моих проектах.

@топакси +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 = function(eventDuration){
eventMDuration = moment.duration(eventDuration, 'секунды');
строка длительности события = ""
если (eventMDuration.days() > 0)
eventDurationString += " " + moment.duration(eventMDuration.days(), 'дни').humanize()
если (eventMDuration.hours() > 0)
eventDurationString += " " + moment.duration(eventMDuration.hours(), 'часы').humanize()
если (eventMDuration.minutes() > 0)
eventDurationString += " " + moment.duration(eventMDuration.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) , где единицей измерения является одна из ss , s , m , h , d , M .

https://momentjs.com/docs/#/настройка/относительный порог времени/

+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. Решение @rockymeza https://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 ' Этот совет дает «достаточно хорошую» реализацию без необходимости в дополнительной библиотеке (часть _ может быть реализована с использованием простого 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"
    

    (Примечание: что-то не так с форматированием секунд и локалями, это будет исправлено #3981 в #4183.)

Что бы это ни стоило, я только что нашел это в официальной документации:

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

Мой собственный код выглядит так — и он отлично работает:

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

Я думаю, это эффективно закрывает эту проблему ...?

@mercmobily для Австралии, США (Калифорния), Великобритании: да, ROW: не совсем так; возможно, когда https://github.com/codebox/moment-precise-range/issues/6 будет исправлено.

(Примечание: использование moment и *60*1000 кажется мне немного странным, вы не рассматривали moment().add(durationInMinutes, 'minutes') ?)

Ах, я не очень хорошо разбираюсь в Моменте! К чему добавляется 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

На всякий случай, если кто-нибудь попадет сюда после поиска в Google, в моем случае было достаточно этой конфигурации/обходного пути:

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"

потому что точность в моем конкретном случае использования гораздо важнее красивого текста.

Это сравнение до и после настройки/вызова методов 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 рейтинги