Skip to content

Commit 4e81ef9

Browse files
committed
Implement "signMode" on Duration#toFormat
1 parent 5aa55da commit 4e81ef9

File tree

3 files changed

+95
-7
lines changed

3 files changed

+95
-7
lines changed

src/duration.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,13 @@ export default class Duration {
466466
* @param {string} fmt - the format string
467467
* @param {Object} opts - options
468468
* @param {boolean} [opts.floor=true] - floor numerical values
469+
* @param {'negative'|'all'|'negativeLargestOnly'} [opts.signMode=negative] - How to handle signs
469470
* @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("y d s") //=> "1 6 2"
470471
* @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("yy dd sss") //=> "01 06 002"
471472
* @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("M S") //=> "12 518402000"
473+
* @example Duration.fromObject({ days: 6, seconds: 2 }).toFormat("d s", { signMode: "all" }) //=> "+6 +2"
474+
* @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "all" }) //=> "-6 -2"
475+
* @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "negativeLargestOnly" }) //=> "-6 2"
472476
* @return {string}
473477
*/
474478
toFormat(fmt, opts = {}) {

src/impl/formatter.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export default class Formatter {
126126
return this.dtFormatter(dt, opts).resolvedOptions();
127127
}
128128

129-
num(n, p = 0) {
129+
num(n, p = 0, signDisplay = undefined) {
130130
// we get some perf out of doing this here, annoyingly
131131
if (this.opts.forceSimple) {
132132
return padStart(n, p);
@@ -137,6 +137,9 @@ export default class Formatter {
137137
if (p > 0) {
138138
opts.padTo = p;
139139
}
140+
if (signDisplay) {
141+
opts.signDisplay = signDisplay;
142+
}
140143

141144
return this.loc.numberFormatter(opts).format(n);
142145
}
@@ -372,6 +375,7 @@ export default class Formatter {
372375
}
373376

374377
formatDurationFromString(dur, fmt) {
378+
const invertLargest = this.opts.signMode === "negativeLargestOnly" ? -1 : 1;
375379
const tokenToField = (token) => {
376380
switch (token[0]) {
377381
case "S":
@@ -397,8 +401,17 @@ export default class Formatter {
397401
tokenToString = (lildur, info) => (token) => {
398402
const mapped = tokenToField(token);
399403
if (mapped) {
400-
const inversionFactor = info.isNegativeDuration && mapped !== info.largestUnit ? -1 : 1;
401-
return this.num(lildur.get(mapped) * inversionFactor, token.length);
404+
const inversionFactor =
405+
info.isNegativeDuration && mapped !== info.largestUnit ? invertLargest : 1;
406+
let signDisplay;
407+
if (this.opts.signMode === "negativeLargestOnly" && mapped !== info.largestUnit) {
408+
signDisplay = "never";
409+
} else if (this.opts.signMode === "all") {
410+
signDisplay = "always";
411+
} else {
412+
signDisplay = "negative";
413+
}
414+
return this.num(lildur.get(mapped) * inversionFactor, token.length, signDisplay);
402415
} else {
403416
return token;
404417
}

test/duration/format.test.js

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,84 @@ test("Duration#toFormat returns a lame string for invalid durations", () => {
283283
expect(Duration.invalid("because").toFormat("yy")).toBe("Invalid Duration");
284284
});
285285

286-
test("Duration#toFormat shows negative sign on the largest unit", () => {
287-
expect(Duration.fromObject({ years: -3, seconds: -45 }).toFormat("yyss")).toBe("-0345");
286+
// - signMode negativeLargestOnly
287+
288+
test("Duration#toFormat shows negative sign on the largest unit when using signMode negativeLargestOnly", () => {
289+
expect(
290+
Duration.fromObject({ years: -3, seconds: -45 }).toFormat("yyss", {
291+
signMode: "negativeLargestOnly",
292+
})
293+
).toBe("-0345");
288294
expect(
289-
Duration.fromObject({ years: -3, seconds: -45 }).toFormat("'before'yy'between'ss'after'")
295+
Duration.fromObject({ years: -3, seconds: -45 }).toFormat("'before'yy'between'ss'after'", {
296+
signMode: "negativeLargestOnly",
297+
})
290298
).toBe("before-03between45after");
291299
// Intentionally have the seconds not first to make sure years is still picked as the largest unit
292-
expect(Duration.fromObject({ seconds: -45, years: -3 }).toFormat("ssyy")).toBe("45-03");
300+
expect(
301+
Duration.fromObject({ seconds: -45, years: -3 }).toFormat("ssyy", {
302+
signMode: "negativeLargestOnly",
303+
})
304+
).toBe("45-03");
305+
});
306+
307+
test("Duration#toFormat shows no negative sign on the largest unit when using signMode negativeLargestOnly with positive Duration", () => {
308+
expect(
309+
Duration.fromObject({ years: 3, seconds: 45 }).toFormat("yyss", {
310+
signMode: "negativeLargestOnly",
311+
})
312+
).toBe("0345");
313+
expect(
314+
Duration.fromObject({ years: 3, seconds: 45 }).toFormat("'before'yy'between'ss'after'", {
315+
signMode: "negativeLargestOnly",
316+
})
317+
).toBe("before03between45after");
318+
// Intentionally have the seconds not first to make sure years is still picked as the largest unit
319+
expect(
320+
Duration.fromObject({ years: 3, seconds: 45 }).toFormat("ssyy", {
321+
signMode: "negativeLargestOnly",
322+
})
323+
).toBe("4503");
324+
});
325+
326+
// - signMode all
327+
328+
test("Duration#toFormat with signMode all shows positive sign on positive durations", () => {
329+
expect(
330+
Duration.fromObject({ years: 3, seconds: 45 }).toFormat("yyss", {
331+
signMode: "all",
332+
})
333+
).toBe("+03+45");
334+
expect(
335+
Duration.fromObject({ years: 3, seconds: 45 }).toFormat("'before'yy'between'ss'after'", {
336+
signMode: "all",
337+
})
338+
).toBe("before+03between+45after");
339+
// Intentionally have the seconds not first to make sure years is still picked as the largest unit
340+
expect(
341+
Duration.fromObject({ years: 3, seconds: 45 }).toFormat("ssyy", {
342+
signMode: "all",
343+
})
344+
).toBe("+45+03");
345+
});
346+
347+
test("Duration#toFormat with signMode all shows positive sign on negative durations", () => {
348+
expect(
349+
Duration.fromObject({ years: -3, seconds: -45 }).toFormat("yyss", {
350+
signMode: "all",
351+
})
352+
).toBe("-03-45");
353+
expect(
354+
Duration.fromObject({ years: -3, seconds: -45 }).toFormat("'before'yy'between'ss'after'", {
355+
signMode: "all",
356+
})
357+
).toBe("before-03between-45after");
358+
// Intentionally have the seconds not first to make sure years is still picked as the largest unit
359+
expect(
360+
Duration.fromObject({ years: -3, seconds: -45 }).toFormat("ssyy", {
361+
signMode: "all",
362+
})
363+
).toBe("-45-03");
293364
});
294365

295366
//------

0 commit comments

Comments
 (0)