Skip to content

Commit

Permalink
add Duration#toHuman
Browse files Browse the repository at this point in the history
  • Loading branch information
icambron committed Dec 22, 2021
1 parent 43e0140 commit c96b813
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -1824,8 +1824,8 @@ export default class DateTime {
if (!this.isValid) return false;

const inputMs = otherDateTime.valueOf();
const otherZoneDateTime = this.setZone(otherDateTime.zone, { keepLocalTime: true });
return otherZoneDateTime.startOf(unit) <= inputMs && inputMs <= otherZoneDateTime.endOf(unit);
const adjustedToZone = this.setZone(otherDateTime.zone, { keepLocalTime: true });
return adjustedToZone.startOf(unit) <= inputMs && inputMs <= adjustedToZone.endOf(unit);
}

/**
Expand Down
32 changes: 32 additions & 0 deletions src/duration.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,38 @@ export default class Duration {
: INVALID;
}

/**
* Returns a string representation of a Duration with all units included
* To modify its behavior use the `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant. See {@link Intl.NumberFormat}.
* @param dur - the duration to format
* @param locale - A locale string like "en" to override the default locale. If you wish to use the default locale, but have an options argument, you may provide that options argument here instead.
* @param opts - On option object to override the formatting. Accepts the same keys as the options parameter of the native `Int.NumberFormat` constructor, as well as `listStyle`. See {@link DurationHumanizeFormatOpts} for more.
* @example
* ```js
* var dur = duration({ days: 1, hours: 5, minutes: 6 })
* toHuman() //=> '1 day, 5 hours, 6 minutes'
* toHuman({ listStyle: "long" }) //=> '1 day, 5 hours, and 6 minutes'
* toHuman({ unitDisplay: "short" }) //=> '1 day, 5 hr, 6 min'
* ```
*/
toHuman(opts = {}) {
const l = orderedUnits
.map((unit) => {
const val = this.values[unit];
if (isUndefined(val)) {
return null;
}
return this.loc
.numberFormatter({ style: "unit", unitDisplay: "long", ...opts, unit: unit.slice(0, -1) })
.format(val);
})
.filter((n) => n);

return this.loc
.listFormatter({ type: "conjunction", style: opts.listStyle || "narrow", ...opts })
.format(l);
}

/**
* Returns a JavaScript object with this Duration's values.
* @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toObject() //=> { years: 1, days: 6, seconds: 2 }
Expand Down
25 changes: 22 additions & 3 deletions src/impl/locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ import Settings from "../settings.js";
import DateTime from "../datetime.js";
import IANAZone from "../zones/IANAZone.js";

// todo - remap caching

let intlLFCache = {};
function getCachedLF(locString, opts = {}) {
const key = JSON.stringify([locString, opts]);
let dtf = intlLFCache[key];
if (!dtf) {
dtf = new Intl.ListFormat(locString, opts);
intlLFCache[key] = dtf;
}
return dtf;
}

let intlDTCache = {};
function getCachedDTF(locString, opts = {}) {
const key = JSON.stringify([locString, opts]);
Expand Down Expand Up @@ -144,8 +157,10 @@ class PolyNumberFormatter {
this.padTo = opts.padTo || 0;
this.floor = opts.floor || false;

if (!forceSimple) {
const intlOpts = { useGrouping: false };
const { padTo, floor, ...otherOpts } = opts;

if (!forceSimple || Object.keys(otherOpts).length > 0) {
const intlOpts = { useGrouping: false, ...opts };
if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo;
this.inf = getCachedINF(intl, intlOpts);
}
Expand Down Expand Up @@ -308,7 +323,7 @@ export default class Locale {
return this.fastNumbersCached;
}

listingMode(defaultOK = true) {
listingMode() {
const isActuallyEn = this.isEnglish();
const hasNoWeirdness =
(this.numberingSystem === null || this.numberingSystem === "latn") &&
Expand Down Expand Up @@ -421,6 +436,10 @@ export default class Locale {
return new PolyRelFormatter(this.intl, this.isEnglish(), opts);
}

listFormatter(opts = {}) {
return getCachedLF(this.intl, opts);
}

isEnglish() {
return (
this.locale === "en" ||
Expand Down
32 changes: 32 additions & 0 deletions test/duration/format.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,35 @@ test("Duration#toFormat localizes the numbers", () => {
test("Duration#toFormat returns a lame string for invalid durations", () => {
expect(Duration.invalid("because").toFormat("yy")).toBe("Invalid Duration");
});

//------
// #humanize()
//------

test("Duration#toHuman formats out a list", () => {
expect(dur().toHuman()).toEqual(
"1 year, 2 months, 1 week, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds"
);
});

test("Duration#toHuman only shows the units you have", () => {
expect(Duration.fromObject({ years: 3, hours: 4 }).toHuman()).toEqual("3 years, 4 hours");
});

test("Duration#toHuman accepts a listStyle", () => {
expect(dur().toHuman({ listStyle: "long" })).toEqual(
"1 year, 2 months, 1 week, 3 days, 4 hours, 5 minutes, 6 seconds, and 7 milliseconds"
);
});

test("Duration#toHuman accepts number format opts", () => {
expect(dur().toHuman({ unitDisplay: "short" })).toEqual(
"1 yr, 2 mths, 1 wk, 3 days, 4 hr, 5 min, 6 sec, 7 ms"
);
});

test("Duration#toHuman works in differt languages", () => {
expect(dur().reconfigure({ locale: "fr" }).toHuman()).toEqual(
"1 an, 2 mois, 1 semaine, 3 jours, 4 heures, 5 minutes, 6 secondes, 7 millisecondes"
);
});

0 comments on commit c96b813

Please sign in to comment.