Moment: Demande de fonctionnalité : un .humanize plus précis

Créé le 24 juin 2012  ·  112Commentaires  ·  Source: moment/moment

.humanize n'est pas vraiment précis en ce qui concerne les secondes.

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

Je comprends que nous ne voulons pas souvent ce niveau de précision, mais parfois nous voulons la valeur exacte d'une manière lisible par l'homme.

Je suggère une modification de l'API (qui semble rétrocompatible d'après ce que je vois réellement) pour prendre en charge cette fonctionnalité :

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"

Qu'est-ce que tu penses ?

Enhancement New Feature

Commentaire le plus utile

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

Ce serait bien d'avoir une sortie précise comme: 3 heures et 3 secondes.

Tous les 112 commentaires

@FGRibreau , ce problème est survenu dans # 232, et l'OP là-bas semblait être OK pour ne pas ajouter cela au noyau.

Vous pouvez écrire une définition de langage appelée precise-english ou quelque chose pour quand vous voulez des temps relatifs plus précis. Avec la nouvelle fonctionnalité de langage d'instance disponible dans la version 1.7.0, cela pourrait être très utile. À l'heure actuelle, toute définition de langue héritera de toutes les valeurs non spécifiées de l'anglais, il vous suffira donc de modifier le dictionnaire relativeTime. Malheureusement, l'héritage n'hérite pas dans les dictionnaires, vous devez donc spécifier l'ensemble du dictionnaire.

Attention : la méthode décrite en #232 ne fonctionnera plus en 1.7.0, à cause des changements en #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();

Une chose qui faciliterait cela serait que l'héritage fonctionne de sorte que vous n'ayez qu'à spécifier une seule valeur à l'intérieur de l'objet relativeTime . @timrwood , pensez-vous que nous avons besoin d'un héritage _plus intelligent_ ?

Si nous ajoutons un héritage plus intelligent, je voudrais éventuellement faire quelque chose comme le fait CLDR et faire en sorte que chaque sous-langage hérite du langage maître. Donc fr_CA hériterait de fr et en_GB hériterait de en .

Je ne pense pas que nous ayons besoin de faire une extension profonde pour ne changer que 'relativeTime.s'.

@FGRibreau a fait quelque chose dans le sens d'une définition de langage precise-en pour vous ?

Fermer cela en tant que solution de @rockymeza est la meilleure façon de gérer cela.

Cela s'applique à plus que quelques secondes. Il en va de même pour les heures, les années, etc...

Oui, et la solution préférée est préférée uniquement pour les auditifs anglophones. Dans d'autres langues, vous devez vous préoccuper des inflexions (déclinaisons) des noms.

J'ajoute du poids pour ce problème. Lors de la conception d'un compte à rebours, la précision est nécessaire.

Donc, c'est OK d'avoir "il y a 10 heures" quand on regarde les choses passées, mais si j'attends avec impatience un événement, je veux "dans 10 heures 3 minutes 8 secondes"

Ne me dites pas que je dois écrire un langage personnalisé pour obtenir cela ?

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

Ce serait bien d'avoir une sortie précise comme: 3 heures et 3 secondes.

Je veux aussi cette fonctionnalité.

J'ai aussi besoin d'une version humanisée précise!

Je pense aussi que ce serait bien que l'humanisation de Moment soit (éventuellement) plus précise, mais pour tous ceux qui sont venus ici à la recherche d'une solution en ce moment, j'ai trouvé que HumanizeDuration.js fonctionne plutôt bien :

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'

J'adorerais voir une option "humanisation précise", devoir utiliser une bibliothèque supplémentaire en plus du moment est un peu maladroit.

+1 pour une humanisation précise !

Vraiment déçu de voir qu'il n'y a pas d'option pour obtenir la durée humanisée précise. :|

Il existe un plugin appelé Precise Range référencé dans la documentation. Quelqu'un l'a-t-il essayé ?

Precise Range fonctionne techniquement, mais il semble renvoyer une chaîne au lieu d'un objet moment, donc je ne peux pas choisir précisément les parties de date que je pourrais aimer.

La langue (imo) est un peu décalée aussi, un résultat était - "une heure 19 minutes quelques secondes". Je préférerais voir quelque chose comme 1 heure 19 minutes et quelques secondes. Mélanger des mots et des chiffres a toujours été un peu un non-non grammatical (pour autant que je sache).

+1 pour un moment précis humaniser

@Envek Precise Range semble sympa, ne supporte cependant aucun i18n/l10n ...

Le support officiel de moment.js serait bien car j'en avais besoin dans au moins deux ou trois de mes projets.

@topaxi +1

+1

@doingweb Je confirme que le module HumanizeDuration.js fonctionne plutôt bien !

+1 pour un moment précis humaniser

+1 pour humaniser un moment plus précis

+1

+1

+1

+1

+1

+1

+1

Pourquoi est-ce que ça se ferme tout le temps ? C'est la fonctionnalité la plus évidente qui doit être implémentée dans moment.js. En fait, j'ai été choqué qu'il ne soit pas déjà dedans. Humanize est tout simplement inutile ailleurs qu'un affichage vague de l'heure comme dans les messages sur les forums sans cela.

+1

+1

+1

-1. J'aime humaniser la façon dont cela fonctionne maintenant. Il est censé donner une version "humaine" d'une durée.

"Hey Frank, quand as-tu envoyé cet e-mail ?"
"Il y a environ une minute."

vs

"Hey Frank, quand as-tu envoyé cet e-mail ?"
"Il y a quarante-trois secondes."

Qu'est-ce qui semble plus "humain?"

Une version plus précise n'était pas censée remplacer l'actuelle. C'est une demande de nouvelle fonctionnalité.

+1, @mattgrande alors que ce que nous avons maintenant est idéal pour décrire la durée de quelque chose dans le passé, ce n'est pas approprié pour décrire, disons par exemple la durée d'un rendez-vous, par exemple "1 heure, 30 minutes".

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1 pour ça de ma part. Faire une nouvelle définition de langage, ou en modifier un existant, ne fonctionne pas vraiment. Il semble qu'il y ait actuellement 108 langues prises en charge par Moment, nous devons donc soit modifier 108 définitions manuellement (et espérons que nous les obtiendrons toutes grammaticalement correctes), soit simplement oublier les utilisateurs qui ne parlent pas anglais ? Aucune de ces solutions ne semble être une bonne solution...

J'utilise ceci :

````javascript
var humanizeDuration = function(eventDuration){
eventMDuration = moment.duration(eventDuration, 'seconds');
eventDurationString = ""
si (eventMDuration.jours() > 0)
eventDurationString += " " + moment.duration(eventMDuration.days(), 'days').humanize()
si (eventMDuration.hours() > 0)
eventDurationString += " " + moment.duration(eventMDuration.hours(), 'hours').humanize()
si (eventMDuration.minutes() > 0)
eventDurationString += " " + moment.duration(eventMDuration.minutes(), 'minutes').humanize()

return eventDurationString.trim()
}
````

+1

+1 sans cette fonctionnalité, le suivi du temps n'est pas possible.

Pourquoi le problème est-il clos ?

+1

+1

+1

La possibilité d'ajouter éventuellement une option plus précise, tout en restant "humaine", serait extrêmement précieuse.

+1

+1

+1

+1

+1. Travail sur une application comptable qui nécessite d'afficher une durée avec précision mais dans un format humain. "1 an, 2 mois, 3 jours" est mieux que "398 jours", mais "1 an" n'est pas vraiment acceptable.

L'implémentation actuelle est si humaine qu'elle est fondamentalement inutile.

+1

duration.humanize a des seuils qui définissent quand une unité est considérée comme une minute, une heure, etc. Par exemple, par défaut, plus de 45 secondes sont considérées comme une minute, plus de 22 heures sont considérées comme un jour, etc. Pour modifier ces seuils, utilisez moment.relativeTimeThreshold(unit, limit) où l'unité est l'une des ss , s , m , h , d , M .

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

+1

+1

+1, veuillez reconsidérer l'ouverture de ce problème 😍

+1

+1

+1

+1

+1

+1

J'utilise quelque chose comme ça :

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"

Utilise lodash et pluralize .

@timrwood un cas pour mettre en œuvre ceci :

  1. La solution https://github.com/moment/moment/issues/348#issuecomment -6535794 de @rockymeza signifie que nous (tous les développeurs) devons réimplémenter cela dans toutes les langues qu'ils prennent en charge, même si vous et votre les contributeurs ont déjà fait le travail incroyable de traduire dans de nombreuses langues
  2. @mattgrande https://github.com/moment/moment/issues/348#issuecomment -237830415 argument selon lequel c'est déjà assez humain, appelle simplement la partie qui fonctionne bien. Considérez cette conversation :

Salut Frank, je vais déjeuner, combien de temps me reste-t-il jusqu'à la prochaine réunion ?
une heure

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. Merci à @sproot d'avoir appelé 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"
    

    C'est un peu mieux, mais cela reste problématique lorsque la durée de la pause est supérieure à 60 minutes. Cette différence d'environ 30 minutes peut signifier que je peux rentrer chez moi, manger et revenir, ou que je dois prendre quelque chose à proximité.

  2. Solutions de contournement de @Nerian https://github.com/moment/moment/issues/348#issuecomment -279819773 et @cogwirrel https://github.com/moment/moment/issues/348#issuecomment -346233713 combiné avec @sproot ' s tip obtient une implémentation "assez bonne" sans avoir besoin d'une bibliothèque supplémentaire (les parties _ peuvent être implémentées à l'aide de JS simple), mais c'est un hack pour quelque chose qui pourrait être pris en charge directement :

    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"
    

    (Remarque : Il y a un problème avec le formatage des secondes et les paramètres régionaux, qui sera corrigé par #3981 dans #4183.)

Pour ce que ça vaut, je viens de trouver ceci dans la documentation officielle :

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

Mon propre code ressemble à ceci -- et il fonctionne parfaitement :

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

Je suppose que cela ferme efficacement ce problème ...?

@mercmobily pour AU, US (CA), UK : oui, ROW : pas vraiment ; peut-être quand https://github.com/codebox/moment-precise-range/issues/6 est corrigé.

(Sidenote : utiliser moment et *60*1000 me semble un peu étrange, avez-vous pensé moment().add(durationInMinutes, 'minutes') ?)

Ah, je ne connais pas très bien Moment ! À quoi s'ajoute moment().add(durationInMinutes, 'minutes') ?
Je pense qu'une fois ce problème clos, c'est "la" solution à ce problème...

moment() est "maintenant" (similaire à new Date() ), et add l'augmente de n minutes. Cependant, n'importe quel objet de moment peut être utilisé, sans se limiter à "maintenant". (Afin de garder cette discussion déjà longue concentrée, laissez ceci être le dernier commentaire lié à cette sidenote ;)

+1

+2

+1

+1

+1

Veuillez rouvrir ceci. Mon logiciel aimerait afficher correctement n'importe quel type d'intervalle de temps, et changer "toutes les 12 secondes" en "toutes les quelques secondes" ou changer "toutes les 54 secondes" en "toutes les minutes" n'est pas acceptable.

+1

+1

Je fais:

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

Et je voudrais qu'il dise : '5 secondes' mais à la place il dit 'quelques secondes'. Quand pouvons-nous ajouter ce support ?

+1

+1

Voici une petite fonction utilitaire basée sur celle de @cogwirrel qui n'utilise pas 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

(Je dirais que si vous allez avoir une méthode humanize(), alors convertir "1,51 heures" en "2 heures" n'est pas particulièrement humain).

+1 😞
D'autres bibliothèques ne prennent pas en charge une telle variété de langages comme le fait Moment.js. Veuillez reconsidérer !

+1

+1

+1

+1

PS incroyable qu'il n'est pas encore implémenté ;(

+1 vraiment, ce n'est toujours pas là ? !

Je me désabonne enfin (ça fait à peu près 7 ans déjà, mais je pense que j'ai enfin réussi avec tous ces +1)

J'ai trouvé un package qui résout mes problèmes 🎉
https://github.com/EvanHahn/HumanizeDuration.js

Peut-être que c'est utile pour quelqu'un d'autre aussi.

+1

+1

+1

+1

Mon avis sur ce problème :

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

Cela a beaucoup de sens, j'aimerais voir un formatage précis d'une durée.

J'adorerais l'option d'humaniser sans arrondir
+1

+1 sur ce

+1

+1

+1

+1

Juste au cas où quelqu'un atterrirait ici après avoir cherché sur Google, cette configuration/solution de contournement était suffisante dans mon cas :

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"

parce que la précision dans mon cas d'utilisation particulier est beaucoup plus importante qu'un beau texte.

Voici une comparaison avant et après la configuration/appel des méthodes moment.relative*() :

| durée | avant | après |
| --------- | --------------- | -------------- |
| 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 |

Cette page vous a été utile?
0 / 5 - 0 notes