Moment: Solicitud de función: un .humanize más preciso

Creado en 24 jun. 2012  ·  112Comentarios  ·  Fuente: moment/moment

.humanize no es realmente preciso cuando se trata de segundos.

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

Entiendo que a menudo no queremos este nivel de precisión, pero a veces queremos el valor exacto en una forma legible por humanos.

Sugiero un cambio en la API (que parece compatible con versiones anteriores de lo que realmente veo) para admitir esta función:

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é piensas ?

Enhancement New Feature

Comentario más útil

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

Sería bueno tener una salida precisa como: 3 horas y 3 segundos.

Todos 112 comentarios

@FGRibreau , este problema surgió en el n. ° 232, y el OP parecía estar bien al no agregar eso al núcleo.

Podría escribir una definición de idioma llamada precise-english o algo así para cuando desee tiempos relativos más precisos. Con la nueva función de idioma de instancia que se lanzará en 1.7.0, podría ser bastante útil. En este momento, cualquier definición de idioma heredará todos los valores no especificados del inglés, por lo que solo tendría que cambiar el diccionario de tiempo relativo. Desafortunadamente, la herencia no se hereda dentro de los diccionarios, por lo que debe especificar todo el diccionario.

Tenga en cuenta: el método descrito en #232 ya no funcionará en 1.7.0, debido a los cambios 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();

Una cosa que facilitaría esto sería que la herencia funcione para que solo tenga que especificar un valor dentro del objeto relativeTime . @timrwood , ¿crees que necesitamos una herencia _más inteligente_?

Si agregamos una herencia más inteligente, posiblemente me gustaría hacer algo como CLDR y hacer que cada subidioma herede del idioma maestro. Entonces fr_CA heredaría de fr y en_GB heredaría de en .

Sin embargo, no creo que necesitemos hacer una extensión profunda para cambiar solo 'relativeTime.s'.

@FGRibreau hizo algo similar a una definición de lenguaje precise-en que funcionó para usted.

Cerrar esto como la solución de @rockymeza es la forma preferida de manejar esto.

Esto se aplica a algo más que unos segundos. Lo mismo se aplica a las horas, años, etc...

Sí, y la solución preferida se prefiere solo para personas de habla inglesa. En otros idiomas tienes que preocuparte por las inflexiones de los sustantivos (declinaciones).

Agrego peso para este problema. Al diseñar una cuenta regresiva, se necesita precisión.

Por lo tanto, está bien tener "hace 10 horas" cuando miro cosas pasadas, pero si espero ansiosamente un evento, quiero "en 10 horas, 3 minutos y 8 segundos".

¿No me digas que tengo que escribir un idioma personalizado para obtener eso?

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

Sería bueno tener una salida precisa como: 3 horas y 3 segundos.

Yo también quiero esta característica.

¡También necesito una versión humanizada precisa!

También creo que sería bueno que la humanización de Moment fuera (opcionalmente) más precisa, pero para cualquiera que haya venido aquí buscando una solución en este momento, descubrí que HumanizeDuration.js funciona bastante 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'

Me encantaría ver una opción de "humanización precisa", tener que usar una biblioteca adicional además del momento es un poco torpe.

¡+1 para humanizar con precisión!

Realmente decepcionado al ver que no hay opción para obtener la duración humanizada precisa. :|

Hay un complemento llamado Precise Range al que se hace referencia en los documentos. ¿Alguien lo ha probado?

Precise Range técnicamente funciona, pero parece devolver una cadena en lugar de un objeto de momento, por lo que no puedo elegir específicamente qué partes de fecha me gustarían.

El idioma (imo) también está un poco fuera de lugar, un resultado fue: 'una hora, 19 minutos, unos pocos segundos'. Preferiría ver algo así como 1 hora 19 minutos y unos segundos. Mezclar palabras y números siempre ha sido un poco gramaticalmente prohibido (hasta donde yo sé).

+1 por momento preciso humanizar

@Envek Precise Range parece agradable, aunque no es compatible con ningún i18n/l10n...

El soporte oficial de moment.js sería bueno ya que lo necesitaba en al menos dos o tres de mis proyectos.

@topaxi +1

+1

@doingweb ¡Confirmo que el módulo HumanizeDuration.js funciona bastante bien!

+1 por momento preciso humanizar

+1 para un momento más preciso humanizar

+1

+1

+1

+1

+1

+1

+1

¿Por qué esto se cierra todo el tiempo? Esta es la característica más obvia que debe implementarse en moment.js. De hecho, me sorprendió que no esté ya. Humanize es simplemente inútil en cualquier lugar que no sea una visualización vaga del tiempo, como en publicaciones en foros sin él.

+1

+1

+1

-1. Me gusta humanizar cómo funciona ahora. Se supone que debe dar una versión "amigable para los humanos" de una duración.

"Hola Frank, ¿cuándo enviaste ese correo electrónico?"
"Hace alrededor de un minuto."

contra

"Hola Frank, ¿cuándo enviaste ese correo electrónico?"
"Hace cuarenta y tres segundos".

¿Cuál parece más "humano"?

Se suponía que una versión más precisa no reemplazaría a la actual. Es una solicitud de nueva función.

+1, @mattgrande si bien lo que tenemos ahora es excelente para describir la duración de algo en el pasado, no es apropiado para describir, por ejemplo, la duración de una cita, por ejemplo, "1 hora, 30 minutos".

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1 por esto de mi parte. Hacer una nueva definición de idioma o modificar uno existente, realmente no funciona. Parece que Moment actualmente admite 108 idiomas, por lo que debemos modificar 108 definiciones manualmente (y esperamos que todas sean gramaticalmente correctas) o simplemente olvidarnos de los usuarios que no hablan inglés. Ninguna de esas suena como una buena solución...

estoy usando esto:

````javascript
var humanizeDuration = function(eventDuration){
eventMDuration = moment.duration(eventDuration, 'segundos');
eventDurationString = ""
si (eventoMDuración.días() > 0)
eventDurationString += " " + moment.duration(eventMDuration.days(), 'days').humanize()
if (eventoMDuración.horas() > 0)
eventDurationString += " " + moment.duration(eventMDuration.horas(), 'horas').humanize()
if (eventoMDuración.minutos() > 0)
eventDurationString += " " + moment.duration(eventMDuration.minutes(), 'minutos').humanize()

devolver eventDurationString.trim()
}
````

+1

+1 sin esta característica, el seguimiento del tiempo no es posible.

¿Por qué el tema está cerrado?

+1

+1

+1

La capacidad de agregar opcionalmente una opción más precisa, aunque aún 'humana', sería muy valiosa.

+1

+1

+1

+1

+1. Trabajando en una aplicación de contabilidad que requiere mostrar una duración con precisión pero en un formato humano. "1 año, 2 meses, 3 días" es mejor que "398 días", sin embargo, "1 año" no es realmente aceptable.

La implementación actual es tan humana que es básicamente inútil.

+1

duration.humanize tiene umbrales que definen cuándo una unidad se considera un minuto, una hora, etc. Por ejemplo, por defecto más de 45 segundos se considera un minuto, más de 22 horas se considera un día y así sucesivamente. Para cambiar esos límites, use moment.relativeTimeThreshold(unit, limit) donde la unidad es una de ss , s , m , h , d , M .

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

+1

+1

+1, reconsidere abrir este problema 😍

+1

+1

+1

+1

+1

+1

Yo uso algo como esto:

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"

Usa lodash y pluralize .

@timrwood un caso para implementar esto:

  1. La solución https://github.com/moment/moment/issues/348#issuecomment -6535794 de @rockymeza significa que nosotros (todos los desarrolladores) tenemos que volver a implementar esto en todos los idiomas compatibles, aunque usted y su los colaboradores ya han hecho el increíble trabajo de traducir a muchos idiomas
  2. @mattgrande https://github.com/moment/moment/issues/348#issuecomment -237830415 argumento de que ya es lo suficientemente humano, solo menciona la parte que funciona bien. Considera esta conversación:

Hola Frank, voy a almorzar, ¿cuánto tiempo tengo hasta la próxima reunión?
una hora

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. Gracias a @sproot por llamar a 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 un poco mejor, pero sigue siendo problemático cuando la duración del descanso es superior a 60 minutos. Esa diferencia de ~30 minutos puede significar que puedo ir a casa, comer y volver, o tengo que tomar algo cerca.

  2. Soluciones alternativas de @Nerian https://github.com/moment/moment/issues/348#issuecomment -279819773 y @cogwirrel https://github.com/moment/moment/issues/348#issuecomment -346233713 combinado con @sproot ' La sugerencia de s obtiene una implementación "suficientemente buena" sin necesidad de una biblioteca adicional (_ las partes se pueden implementar usando JS simple), pero es un truco para algo que podría admitirse directamente:

    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"
    

    (Nota: hay algo mal con el formato de segundos y las configuraciones regionales, eso se arreglará con el #3981 en el #4183).

Por lo que vale, acabo de encontrar esto en la documentación oficial:

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

Mi propio código se ve así, y funciona perfectamente:

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

Supongo que esto efectivamente cierra este problema...?

@mercmobily para AU, EE. UU. (CA), Reino Unido: sí, ROW: no realmente; tal vez cuando se solucione https://github.com/codebox/moment-precise-range/issues/6 .

(Nota al margen: usar moment y *60*1000 me parece un poco extraño, ¿has considerado moment().add(durationInMinutes, 'minutes') ?)

¡Ah, no estoy muy versado en Moment! ¿A qué moment().add(durationInMinutes, 'minutes') ?
Creo que una vez que se cierra ese problema, esa es "la" solución a este problema...

moment() es "ahora" (similar a new Date() ), y add lo aumenta en n minutos. Sin embargo, se puede usar cualquier objeto de momento, sin limitarse a "ahora". (Para mantener enfocada esta discusión que ya es larga, que este sea el último comentario relacionado con esta nota al margen;)

+1

+2

+1

+1

+1

Vuelva a abrir esto. A mi software le gustaría mostrar cualquier tipo de intervalo de tiempo correctamente, y cambiar "cada 12 segundos" a "cada pocos segundos" o cambiar "cada 54 segundos" a "cada minuto" no es aceptable.

+1

+1

Estoy haciendo:

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

Y me gustaría que dijera: '5 segundos' pero en cambio dice 'unos segundos'. ¿Cuándo podemos añadir este soporte?

+1

+1

Aquí hay una pequeña función de utilidad basada en @cogwirrel que no usa 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

(Yo diría que si va a tener un método humanizar (), entonces convertir "1.51 horas" a "2 horas" no es particularmente humano-y).

+1 😞
Otras bibliotecas no admiten una variedad tan grande de lenguajes como lo hace Moment.js. ¡Por favor reconsidera!

+1

+1

+1

+1

PD: increíble, aún no está implementado ;(

+1 en serio, ¿esto todavía no está aquí?

Finalmente me doy de baja (ya han pasado aproximadamente 7 años, pero creo que finalmente lo logré con todos esos +1)

Encontré un paquete que soluciona mis problemas 🎉
https://github.com/EvanHahn/HumanizeDuration.js

Tal vez sea útil para cualquier otra persona también.

+1

+1

+1

+1

Mi opinión sobre ese problema:

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

Esto tiene mucho sentido, me encantaría ver un formato preciso de una duración.

Me encantaría la opción de humanizar sin redondear
+1

+1 en esto

+1

+1

+1

+1

En caso de que alguien llegue aquí después de buscar en Google, esta configuración/solución fue suficiente en mi caso:

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"

porque la precisión en mi caso de uso particular es mucho más importante que un texto atractivo.

Y esta es una comparación antes y después de configurar/invocar métodos moment.relative*() :

| duración | antes | después de |
| --------- | --------------- | -------------- |
| 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 |

¿Fue útil esta página
0 / 5 - 0 calificaciones