Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Formatting duration in multiple time units #4344

Closed
rokoroku opened this issue Dec 7, 2017 · 6 comments
Closed

Feature request: Formatting duration in multiple time units #4344

rokoroku opened this issue Dec 7, 2017 · 6 comments

Comments

@rokoroku
Copy link

rokoroku commented Dec 7, 2017

Description of the Issue and Steps to Reproduce:
It's a subsequent feature request for #4295

  1. Feature Request: Pass relativeTimeThresholds to humanize() as argument #4295 will add support to format duration in momentjs' existing relative formatting behavior.
  2. Formatting duration via .humanize() could be also achieved with multiple time units. e.g.
moment.duration({ s: 100 }).humanize({ s: true }) // '100 seconds'
moment.duration({ s: 100 }).humanize({ m: true }) // '1 minute'
moment.duration({ s: 100 }).humanize({ m: true, s: true }) // '1 minute 40 seconds'
moment.duration({ s: 100 }).humanize({ ms: true }) // '10000 milliseconds'
@marwahaha
Copy link
Member

This is pretty cool!

@ichernev
Copy link
Contributor

@rokoroku how would you handle unit "holes", like {years: true, seconds: true}. Provide more info on edge cases and we can discuss the proposal.

@rokoroku
Copy link
Author

rokoroku commented Jul 3, 2018

relativeTime : {
future : 'in %s',
past : '%s ago',
s : 'a few seconds',
ss : '%d seconds',
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'
},

Since moment already has locale strings for each time units, we can compose the duration with existing unit strings by mathematical modulus(remainder) and floor operation.

@Alanscut
Copy link
Contributor

Alanscut commented May 9, 2020

@ichernev I'm very interested in this proposal! I also read the source code, but as you said, there are "vulnerabilities" in the processing unit. I checked issue #2928, The same is true here. Can we use the idea of asmonths for reference to calculate and obtain the value of milliseconds, and then use mathematical modulus(remainder) and floor operation? Do we need to consider the threshold?Personally, they should be mutually exclusive. Could you please provide direction on this :)?

Fake code

    let ms = this.$ms; 
    this.$d.years = Math.floor(ms / MILLISECONDS_A_YEAR)
    ms %= MILLISECONDS_A_YEAR
    this.$d.months = Math.floor(ms / MILLISECONDS_A_MONTH)
    ms %= MILLISECONDS_A_MONTH
    this.$d.days = Math.floor(ms / MILLISECONDS_A_DAY)
    ms %= MILLISECONDS_A_DAY
    this.$d.hours = Math.floor(ms / MILLISECONDS_A_HOUR)
    ms %= MILLISECONDS_A_HOUR
    this.$d.minutes = Math.floor(ms / MILLISECONDS_A_MINUTE)
    ms %= MILLISECONDS_A_MINUTE
    this.$d.seconds = Math.floor(ms / MILLISECONDS_A_SECOND)
    ms %= MILLISECONDS_A_SECOND
    this.$d.milliseconds = ms

@Pearce-Ropion
Copy link

Pearce-Ropion commented May 16, 2020

I was actually looking for something like this recently. I ended up just writing my own helper function to do this. Maybe this code would help someone implement this in the future. @ichernev Could you explain what you mean by unit "holes"?

import _ from "lodash";
import moment from "moment";

/**
 * @function formatDuration
 * @description Formats a number representing a duration to a humanized version
 * of said duration.
 *
 * @example duration = 90
 *  durationUnit = "minutes"
 *  result = 1 Hour 30 Minutes
 *
 * @param {Number} duration
 * @param {Object} options
 * @property {String} [durationUnit = "minutes"]
 * @property {Boolean} withWeeks - return the number of weeks in the duration.
 *       @example: 1 Month 3 Weeks 2 Days vs 1 Month 23 Days
 * @property {Boolean} useAnd - formats the last unit to be preceded by the word "and"
 * @property {Boolean} [capitalize = true] - capitalize the duration unit string
 *
 * @returns {String} Formatted duration
 */
export const formatDuration = (
    duration,
    { durationUnit = "minutes", withWeeks, useAnd, capitalize = true } = {}
) => {
    const units = [
        "year",
        "month",
        "week",
        "day",
        "hour",
        "minute",
        "second",
        "millisecond",
    ];

    const d = moment.duration(duration, durationUnit);

    const segments = units
        .map(unit => {
            let value = d.get(unit);

            if (withWeeks && unit === "day") {
                value = value % 7;
            }

            if (value > 0) {
                if ((unit === "week" && withWeeks) || unit !== "week") {
                    let unitString = capitalize ? _.capitalize(unit) : unit;
                    if (value > 1) {
                        unitString = `${unitString}s`;
                    }
                    return `${value} ${unitString}`;
                }
            }
        })
        .filter(Boolean);

    if (useAnd) {
        const head = _.initial(segments);
        const tail = _.last(segments);
        return `${head.join(" ")} and ${tail}`;
    }

    return segments.join(" ");
};

@marwahaha
Copy link
Member

Unfortunately we will not take any new features.

See https://momentjs.com/docs/#/-project-status/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants