Moment: Funktionsanfrage: Eine genauere .humanize

Erstellt am 24. Juni 2012  ·  112Kommentare  ·  Quelle: moment/moment

.humanize ist nicht wirklich genau, wenn es um Sekunden geht.

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

Ich verstehe, dass wir dieses Maß an Genauigkeit nicht oft wollen, aber manchmal wollen wir den genauen Wert in einer für Menschen lesbaren Weise.

Ich schlage eine Änderung an der API vor (die abwärtskompatibel zu dem scheint, was ich tatsächlich sehe), um diese Funktion zu unterstützen:

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"

Was denken Sie ?

Enhancement New Feature

Hilfreichster Kommentar

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

Wäre schön, eine genaue Ausgabe zu haben wie: 3 Stunden und 3 Sekunden.

Alle 112 Kommentare

@FGRibreau , dieses Problem tauchte in # 232 auf, und das OP dort drüben schien in Ordnung zu sein, das nicht zum Kern hinzuzufügen.

Sie könnten eine Sprachdefinition mit dem Namen precise-english oder so etwas schreiben, wenn Sie genauere relative Zeiten wünschen. Mit der neuen Instanz-Lang-Funktion, die in 1.7.0 herauskommt, könnte sie ziemlich nützlich sein. Im Moment erbt jede Sprachdefinition alle nicht angegebenen Werte von Englisch, sodass Sie nur das relativeTime-Wörterbuch ändern müssten. Leider vererbt sich die Vererbung nicht innerhalb von Wörterbüchern, daher müssen Sie das gesamte Wörterbuch angeben.

Bitte beachten Sie: Die in #232 beschriebene Methode wird in 1.7.0 aufgrund der Änderungen in #332 nicht mehr funktionieren.

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();

Eine Sache, die dies erleichtern würde, wäre, dass die Vererbung so funktioniert, dass Sie nur einen Wert innerhalb des relativeTime -Objekts angeben müssen. @timrwood , denkst du, wir brauchen eine _intelligentere_ Vererbung?

Wenn wir eine intelligentere Vererbung hinzufügen, möchte ich möglicherweise etwas wie CLDR tun und jede Untersprache von der Mastersprache erben lassen. Also würde fr_CA von fr erben und en_GB würde von en erben.

Ich glaube jedoch nicht, dass wir tief erweitern müssen, um nur 'relativeTime.s' zu ändern.

@FGRibreau hat etwas in der Art einer precise-en Sprachdefinitionsarbeit für dich gemacht?

Dies als Lösung von @rockymeza zu schließen, ist die bevorzugte Vorgehensweise.

Dies gilt für mehr als nur Sekunden. Dasselbe gilt für Stunden, Jahre usw.

Ja, und die bevorzugte Lösung wird nur für englischsprachige Hörer bevorzugt. In anderen Sprachen muss man sich mit Substantivbeugungen (Deklinationen) herumschlagen.

Ich füge Gewicht für dieses Problem hinzu. Bei der Entwicklung eines Countdowns ist Präzision gefragt.

Es ist also in Ordnung, „vor 10 Stunden“ zu haben, wenn ich auf vergangene Dinge schaue, aber wenn ich gespannt auf ein Ereignis warte, möchte ich „in 10 Stunden, 3 Minuten, 8 Sekunden“.

Sagen Sie mir nicht, dass ich eine benutzerdefinierte Sprache schreiben muss, um das zu erreichen?

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

Wäre schön, eine genaue Ausgabe zu haben wie: 3 Stunden und 3 Sekunden.

Ich möchte diese Funktion auch.

Ich brauche auch eine genaue humanisierte Version!

Ich denke auch, dass es schön wäre, wenn Humanize von Moment (optional) präziser wäre, aber für alle, die hierher gekommen sind, um gerade nach einer Lösung zu suchen, habe ich festgestellt, dass HumanizeDuration.js ziemlich gut funktioniert:

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'

Ich würde gerne eine Option zum "genauen Humanisieren" sehen, da es etwas umständlich ist, zusätzlich eine zusätzliche Bibliothek verwenden zu müssen.

+1 für präzise Humanisierung!

Wirklich enttäuscht zu sehen, dass es keine Möglichkeit gibt, die genaue humanisierte Dauer zu erhalten. :|

Es gibt ein Plugin namens Precise Range , auf das in den Dokumenten verwiesen wird. Hat es jemand probiert?

Precise Range funktioniert technisch gesehen, aber es scheint eine Zeichenfolge anstelle eines Momentobjekts zurückzugeben, sodass ich nicht genau auswählen kann, welche Datumsteile mir gefallen könnten.

Die Sprache (imo) ist auch ein bisschen daneben, ein Ergebnis war - 'eine Stunde 19 Minuten ein paar Sekunden' . Ich würde lieber so etwas wie 1 Stunde 19 Minuten und ein paar Sekunden sehen. Das Mischen von Wörtern und Zahlen war schon immer ein grammatikalisches Tabu (soweit ich weiß).

+1 für den genauen Moment humanisieren

@Envek Precise Range scheint nett zu sein, unterstützt jedoch keine i18n / l10n ...

Offizielle Unterstützung durch moment.js wäre schön, da ich diese in mindestens zwei oder drei meiner Projekte benötigte.

@topaxi +1

+1

@doingweb Ich bestätige, dass das Modul HumanizeDuration.js ziemlich gut funktioniert!

+1 für den genauen Moment humanisieren

+1 für einen genaueren Moment humanisieren

+1

+1

+1

+1

+1

+1

+1

Warum wird das die ganze Zeit geschlossen? Dies ist die offensichtlichste Funktion, die in moment.js implementiert werden muss. Ich war wirklich schockiert, dass es nicht schon drin ist. Humanize ist einfach nutzlos, abgesehen von einer vagen Zeitanzeige wie in Beiträgen in Foren ohne es.

+1

+1

+1

-1. Ich mag Humanize, wie es jetzt funktioniert. Es soll eine "menschenfreundliche" Version einer Dauer geben.

"Hey Frank, wann hast du diese E-Mail gesendet?"
"Vor ungefähr einer Minute."

vs

"Hey Frank, wann hast du diese E-Mail gesendet?"
"Vor dreiundvierzig Sekunden."

Was scheint "menschlicher" zu sein?

Eine genauere Version sollte die aktuelle nicht ersetzen. Es ist eine Anfrage für ein neues Feature.

+1, @mattgrande Während das, was wir jetzt haben, großartig ist, um die Dauer von etwas in der Vergangenheit zu beschreiben, ist es nicht geeignet, um beispielsweise die Länge eines Termins zu beschreiben, z. B. "1 Stunde, 30 Minuten".

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1 dafür von mir. Das Erstellen einer neuen Sprachdefinition oder das Ändern einer vorhandenen funktioniert nicht wirklich. Es sieht so aus, als würden derzeit 108 Sprachen von Moment unterstützt, also müssen wir entweder 108 Definitionen manuell ändern (und hoffen, dass wir sie alle grammatikalisch richtig hinbekommen) oder einfach alle Benutzer vergessen, die kein Englisch sprechen? Beides klingt nicht nach einer guten Lösung...

Ich benutze das:

````javascript
var humanizeDuration = function(eventDuration){
eventMDuration = moment.duration (eventDuration, 'Sekunden');
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() zurückgeben
}
````

+1

+1 Ohne diese Funktion ist keine Zeiterfassung möglich.

Warum ist das Thema geschlossen?

+1

+1

+1

Die Möglichkeit, optional eine präzisere, aber immer noch „menschliche“ Option hinzuzufügen, wäre sehr wertvoll.

+1

+1

+1

+1

+1. Arbeiten an einer Buchhaltungsanwendung, bei der eine Dauer genau, aber in einem menschlichen Format angezeigt werden muss. "1 Jahr, 2 Monate, 3 Tage" ist besser als "398 Tage", aber "1 Jahr" ist nicht wirklich akzeptabel.

Die derzeitige Implementierung ist so menschlich, dass sie im Grunde nutzlos ist.

+1

duration.humanize hat Schwellenwerte, die definieren, wann eine Einheit als Minute, Stunde usw. betrachtet wird. Beispielsweise gelten standardmäßig mehr als 45 Sekunden als eine Minute, mehr als 22 Stunden als ein Tag und so weiter. Um diese Grenzwerte zu ändern, verwenden Sie moment.relativeTimeThreshold(unit, limit) , wobei die Einheit entweder ss , s , m , h , d ist , M .

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

+1

+1

+1, bitte erwägen Sie, diese Ausgabe zu öffnen 😍

+1

+1

+1

+1

+1

+1

Ich benutze so etwas:

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"

Verwendet lodash und pluralize .

@timrwood ein Fall für die Umsetzung:

  1. Die https://github.com/moment/moment/issues/348#issuecomment -6535794 -Lösung von @rockymeza bedeutet, dass wir (jeder einzelne Entwickler da draußen) dies in jeder unterstützten Sprache neu implementieren müssen, obwohl Sie und Ihre Mitwirkende haben bereits die erstaunliche Arbeit geleistet, in viele Sprachen zu übersetzen
  2. @mattgrande https://github.com/moment/moment/issues/348#issuecomment -237830415 Argument, dass es bereits menschlich genug ist, ruft nur den Teil auf, der irgendwie gut funktioniert. Betrachten Sie dieses Gespräch:

Hey Frank, ich gehe Mittag essen, wie viel Zeit habe ich bis zum nächsten Treffen?
eine Stunde

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. Danke an @sproot für den Aufruf von 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"
    

    Es ist etwas besser, aber immer noch problematisch, wenn die Pausenlänge länger als 60 Minuten ist. Dieser ~30-Minuten-Unterschied kann bedeuten, dass ich nach Hause gehen, essen und wiederkommen kann, oder ich muss etwas in der Nähe holen.

  2. Workarounds von @Nerian https://github.com/moment/moment/issues/348#issuecomment -279819773 und @cogwirrel https://github.com/moment/moment/issues/348#issuecomment -346233713 kombiniert mit @sproot ' s tip erhält eine "gut genug" Implementierung, ohne dass eine zusätzliche Bibliothek benötigt wird (_ Teile können mit einfachem JS implementiert werden), aber es ist ein Hack für etwas, das direkt unterstützt werden könnte:

    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"
    

    (Hinweis: Irgendetwas stimmt nicht mit der Sekundenformatierung und den Gebietsschemas, das wird durch #3981 in #4183 behoben.)

Für das, was es wert ist, habe ich das gerade aus der offiziellen Dokumentation gefunden:

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

Mein eigener Code sieht so aus - und er funktioniert perfekt:

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

Ich denke, das schließt dieses Problem effektiv ...?

@mercmobily für AU, US (CA), UK: ja, ROW: nicht wirklich; vielleicht wenn https://github.com/codebox/moment-precise-range/issues/6 behoben ist.

(Nebenbemerkung: Die Verwendung moment und *60*1000 sieht für mich etwas seltsam aus, haben Sie an moment().add(durationInMinutes, 'minutes') gedacht?)

Ah, ich bin nicht sehr versiert in Moment! Was trägt moment().add(durationInMinutes, 'minutes') dazu bei?
Ich denke, sobald dieses Problem geschlossen ist, ist das "die" Lösung für dieses Problem ...

moment() ist "jetzt" (ähnlich wie new Date() ) und add erhöht das um n Minuten. Es kann jedoch jedes Moment-Objekt verwendet werden, das nicht auf "jetzt" beschränkt ist. (Um diese bereits lange Diskussion fokussiert zu halten, lassen Sie dies den letzten Kommentar zu dieser Randnotiz sein ;)

+1

+2

+1

+1

+1

Bitte öffnen Sie diese erneut. Meine Software möchte jedes Zeitintervall korrekt anzeigen, und das Ändern von "alle 12 Sekunden" in "alle paar Sekunden" oder das Ändern von "alle 54 Sekunden" in "jede Minute" ist nicht akzeptabel.

+1

+1

Ich mache:

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

Und ich würde gerne sagen: „5 Sekunden“, aber stattdessen heißt es „ein paar Sekunden“. Wann können wir diese Unterstützung hinzufügen?

+1

+1

Hier ist eine kleine Hilfsfunktion, die auf der von @cogwirrel basiert und lodash nicht verwendet

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

(Ich würde argumentieren, dass, wenn Sie eine humanize () -Methode haben, die Umwandlung von "1,51 Stunden" in "2 Stunden" nicht besonders menschlich ist).

+1 😞
Andere Bibliotheken unterstützen nicht so viele Sprachen wie Moment.js. Bitte ueberlege es Dir nochmal!

+1

+1

+1

+1

PS unglaublich es ist noch nicht implementiert ;(

+1 Wirklich, das ist immer noch nicht hier?!

Ich melde mich endlich ab (es ist schon ungefähr 7 Jahre her, aber ich glaube, ich hatte es endlich mit all diesen +1s)

Ich habe ein Paket gefunden, das meine Probleme löst 🎉
https://github.com/EvanHahn/HumanizeDuration.js

Vielleicht ist es auch für jemand anderen nützlich.

+1

+1

+1

+1

Meine Meinung zu diesem 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(', ');
}

Das macht sehr viel Sinn, würde gerne eine genaue Formatierung einer Dauer sehen.

Würde die Option lieben, ohne Rundung zu humanisieren
+1

+1 dazu

+1

+1

+1

+1

Nur für den Fall, dass jemand nach dem Googeln hier landet, diese Konfiguration/Workaround war in meinem Fall ausreichend:

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"

weil Genauigkeit in meinem speziellen Anwendungsfall viel wichtiger ist als ein gut aussehender Text.

Und dies ist ein Vergleich vor und nach dem Konfigurieren/Aufrufen moment.relative*() -Methoden:

| Dauer | vor | nach |
| --------- | --------------- | -------------- |
| 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 |

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen