Moment: Solicitação de recurso: um .humanize mais preciso

Criado em 24 jun. 2012  ·  112Comentários  ·  Fonte: moment/moment

.humanize não é muito preciso quando 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"

Entendo que muitas vezes não queremos esse nível de precisão, mas às vezes queremos o valor exato de uma maneira legível por humanos.

Sugiro uma alteração na API (que parece compatível com versões anteriores do que realmente vejo) para oferecer suporte a esse recurso:

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"

O que você acha ?

Enhancement New Feature

Comentários muito úteis

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

Seria bom ter uma saída precisa como: 3 horas e 3 segundos.

Todos 112 comentários

@FGRibreau , esse problema surgiu em # 232, e o OP ali parecia estar bem em não adicionar isso ao núcleo.

Você pode escrever uma definição de linguagem chamada precise-english ou algo assim para quando quiser tempos relativos mais precisos. Com o novo recurso lang de instância sendo lançado na versão 1.7.0, pode ser bastante útil. No momento, qualquer definição de idioma herdará todos os valores não especificados do inglês, então você teria que alterar o dicionário relativeTime. Infelizmente, a herança não herda dentro de dicionários, então você tem que especificar o dicionário inteiro.

Observe: o método descrito em #232 não funcionará mais em 1.7.0, devido às alterações em #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();

Uma coisa que tornaria isso mais fácil seria que a herança funcionasse para que você só precisasse especificar apenas um valor dentro do objeto relativeTime . @timrwood , você acha que precisamos de herança _mais inteligente_?

Se adicionarmos uma herança mais inteligente, eu gostaria de possivelmente fazer algo como o CLDR e ter cada sublinguagem herdada da linguagem mestre. Então fr_CA herdaria de fr e en_GB herdaria de en .

Eu não acho que precisamos fazer uma extensão profunda para mudar apenas 'relativeTime.s'.

@FGRibreau fez algo como uma definição de linguagem precise-en para você?

Fechar isso como a solução de @rockymeza é a maneira preferida de lidar com isso.

Isso se aplica a mais do que apenas alguns segundos. O mesmo se aplica a horas, anos, etc...

Sim, e a solução preferida é preferida apenas para o auditivo de língua inglesa. Em outras línguas você tem que se preocupar com flexões de substantivos (declinações).

Eu adiciono peso para esta questão. Ao conceber uma contagem regressiva, a precisão é necessária.

Portanto, não há problema em ter "10 horas atrás" ao olhar para as coisas do passado, mas se estiver esperando ansiosamente por um evento, quero "em 10 horas, 3 minutos e 8 segundos"

Não me diga que eu tenho que escrever uma linguagem personalizada para obter isso?

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

Seria bom ter uma saída precisa como: 3 horas e 3 segundos.

Eu também quero esse recurso.

Eu também preciso de uma versão humanizada precisa!

Também acho que seria bom que o humanize do Moment fosse (opcionalmente) mais preciso, mas para quem veio aqui procurando uma solução agora, descobri que HumanizeDuration.js funciona muito bem:

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'

Adoraria ver uma opção de "humanização precisa", ter que usar uma biblioteca adicional em cima do momento é um pouco desajeitado.

+1 para humanização precisa!

Realmente decepcionado ao ver que não há opção para obter a duração precisa e humanizada. :|

Existe algum plugin chamado Precise Range referenciado nos documentos. Alguém já experimentou?

O Precise Range tecnicamente funciona, mas parece retornar uma string em vez de um objeto moment, então não posso escolher especificamente quais partes de data eu gostaria.

A linguagem (imo) também está um pouco errada, um resultado foi - 'uma hora 19 minutos alguns segundos' . Eu preferiria ver algo como 1 hora 19 minutos e alguns segundos. Misturar palavras e números sempre foi um pouco gramaticalmente proibido (até onde eu sei).

+1 para humanizar momento preciso

@Envek Precise Range parece bom, mas não suporta nenhum i18n/l10n ...

O suporte oficial do moment.js seria bom, pois eu precisava disso em pelo menos dois ou três dos meus projetos.

@topaxi +1

+1

@doingweb Confirmo que o módulo HumanizeDuration.js funciona muito bem!

+1 para humanizar momento preciso

+1 para humanizar momento mais preciso

+1

+1

+1

+1

+1

+1

+1

Por que isso está sendo fechado o tempo todo? Este é o recurso mais óbvio que deve ser implementado no moment.js. Eu estava realmente chocado que não está já. Humanize é simplesmente inútil em qualquer lugar que não seja uma exibição de tempo vaga, como em postagens em fóruns sem ele.

+1

+1

+1

-1. Eu gosto de humanizar como funciona agora. É suposto dar uma versão "amigável para humanos" de uma duração.

"Ei Frank, quando você enviou esse e-mail?"
"Cerca de um minuto atrás."

vs

"Ei Frank, quando você enviou esse e-mail?"
"Quarenta e três segundos atrás."

O que parece mais "humano"?

A versão mais precisa não deveria substituir a atual. É um pedido de novo recurso.

+1, @mattgrande , embora o que temos agora seja ótimo para descrever a duração de algo no passado, não é apropriado para descrever, por exemplo, a duração de um compromisso, por exemplo, "1 hora e 30 minutos".

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1 para isso de mim. Fazer uma nova definição de linguagem, ou modificar uma já existente, realmente não funciona. Parece que existem 108 idiomas atualmente suportados pelo Moment, então temos que modificar 108 definições manualmente (e esperamos que todas elas estejam gramaticalmente corretas) ou simplesmente esquecer os usuários que não falam inglês? Nenhuma das duas parece uma boa solução...

Estou usando isso:

````javascript
var humanizeDuration = function(eventDuration){
eventMDuration = moment.duration(eventDuration, 'segundos');
eventDurationString = ""
if (eventoMDuration.days() > 0)
eventDurationString += " " + moment.duration(eventMDuration.days(), 'days').humanize()
if (eventoMDuration.horas() > 0)
eventDurationString += " " + moment.duration(eventMDuration.hours(), 'hours').humanize()
if (eventoMDuration.minutos() > 0)
eventDurationString += " " + moment.duration(eventMDuration.minutes(), 'minutos').humanize()

return eventDurationString.trim()
}
````

+1

+1 sem esse recurso, o rastreamento de tempo não é possível.

Por que a questão está encerrada?

+1

+1

+1

A capacidade de adicionar opcionalmente uma opção mais precisa, enquanto ainda 'humana' seria super valiosa.

+1

+1

+1

+1

+1. Trabalhando em um aplicativo de contabilidade que exige exibir uma duração com precisão, mas em formato humano. "1 ano, 2 meses, 3 dias" é melhor do que "398 dias", no entanto, "1 ano" não é realmente aceitável.

A implementação atual é tão humana que é basicamente inútil.

+1

duration.humanize tem limites que definem quando uma unidade é considerada um minuto, uma hora e assim por diante. Por exemplo, por padrão, mais de 45 segundos é considerado um minuto, mais de 22 horas é considerado um dia e assim por diante. Para alterar esses pontos de corte, use moment.relativeTimeThreshold(unit, limit) onde a unidade é ss , s , m , h , d , M .

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

+1

+1

+1, reconsidere abrir esta edição 😍

+1

+1

+1

+1

+1

+1

Eu uso algo assim:

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 e pluralize .

@timrwood um caso para implementar isso:

  1. A solução https://github.com/moment/moment/issues/348#issuecomment -6535794 do @rockymeza significa que nós (todo desenvolvedor por aí) temos que reimplementar isso em todos os idiomas que eles suportam, mesmo que você e seu colaboradores já fizeram o incrível trabalho de traduzir para muitos idiomas
  2. @mattgrande https://github.com/moment/moment/issues/348#issuecomment -237830415 argumento de que já é humano o suficiente, está apenas chamando a parte que funciona bem. Considere esta conversa:

Ei Frank, vou almoçar, quanto tempo tenho até a próxima reunião?
uma 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. Obrigado a @sproot por chamar o 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"
    

    É um pouco melhor, mas ainda é problemático quando a duração do intervalo é superior a 60 minutos. Essa diferença de ~30 minutos pode significar que posso ir para casa, comer e voltar, ou tenho que pegar algo nas proximidades.

  2. Soluções alternativas de @Nerian https://github.com/moment/moment/issues/348#issuecomment -279819773 e @cogwirrel https://github.com/moment/moment/issues/348#issuecomment -346233713 combinado com @sproot ' A dica de s obtém uma implementação "boa o suficiente" sem precisar de uma biblioteca adicional (_ partes podem ser implementadas usando JS simples), mas é um hack para algo que pode ser suportado diretamente:

    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"
    

    (Observação: há algo errado com a formatação de segundos e as localidades, que serão corrigidas por #3981 em #4183.)

Por que vale a pena, acabei de encontrar isso na documentação oficial:

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

Meu próprio código se parece com isso - e funciona perfeitamente:

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

Eu acho que isso efetivamente fecha este problema...?

@mercmobily para AU, EUA (CA), Reino Unido: sim, ROW: não realmente; talvez quando https://github.com/codebox/moment-precise-range/issues/6 for corrigido.

(Nota: usar moment e *60*1000 parece um pouco estranho para mim, você já considerou moment().add(durationInMinutes, 'minutes') ?)

Ah, eu não sou muito versado em Moment! O que moment().add(durationInMinutes, 'minutes') adiciona?
Acho que uma vez que o problema é encerrado, essa é "a" solução para esse problema ...

moment() é "agora" (semelhante a new Date() ), e add aumenta isso em n minutos. Qualquer objeto de momento pode ser usado, porém, não limitado a "agora". (Para manter essa já longa discussão focada, que este seja o último comentário relacionado a esta nota lateral ;)

+1

+2

+1

+1

+1

Por favor, reabra isso. Meu software gostaria de mostrar qualquer tipo de intervalo de tempo corretamente, e alterar "a cada 12 segundos" para "a cada poucos segundos" ou alterar "a cada 54 segundos" para "a cada minuto" não é aceitável.

+1

+1

Estou fazendo:

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

E eu gostaria que dissesse: '5 segundos', mas em vez disso, diz 'alguns segundos'. Quando podemos adicionar esse suporte?

+1

+1

Aqui está uma pequena função utilitária baseada em @cogwirrel que não 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

(Eu diria que, se você tiver um método humanize(), converter "1,51 horas" para "2 horas" não é particularmente humano).

+1 😞
Outras bibliotecas não suportam uma variedade tão grande de linguagens como o Moment.js. Por favor, reconsidere!

+1

+1

+1

+1

PS inacreditável que ainda não foi implementado ;(

+1 realmente, isso ainda não está aqui?!

Estou finalmente cancelando a inscrição (já faz cerca de 7 anos, mas acho que finalmente consegui com todos esses +1s)

Encontrei um pacote que resolve meus problemas 🎉
https://github.com/EvanHahn/HumanizeDuration.js

Talvez seja útil para qualquer outra pessoa também.

+1

+1

+1

+1

Minha opinião sobre esse 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(', ');
}

Isso faz muito sentido, adoraria ver uma formatação precisa de uma duração.

Adoraria a opção de humanizar sem arredondar
+1

+1 neste

+1

+1

+1

+1

Apenas no caso de alguém chegar aqui depois de pesquisar no Google, esta configuração / solução alternativa foi suficiente no meu 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 a precisão no meu caso de uso específico é muito mais importante do que um texto bonito.

Ans esta é uma comparação antes e depois de configurar/invocar os métodos moment.relative*() :

| duração | antes | depois |
| --------- | --------------- | -------------- |
| 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 |

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

Shoroh picture Shoroh  ·  3Comentários

IbraheemAlSaady picture IbraheemAlSaady  ·  3Comentários

nikocraft picture nikocraft  ·  3Comentários

vbullinger picture vbullinger  ·  3Comentários

dogukankotan picture dogukankotan  ·  3Comentários