Skip to content

Commit

Permalink
Signed directives.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Nov 12, 2015
1 parent 1573fa8 commit 370362c
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 66 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ Returns a new [*format* function](#_format) for the given string *specifier*. Th

Directives marked with an asterisk (*) may be affected by the [locale definition](#localeFormat). For `%U`, all days in a new year preceding the first Sunday are considered to be in week 0. For `%W`, all days in a new year preceding the first Monday are considered to be in week 0. Week numbers are computed using [*interval*.count](https://github.com/d3/d3-time#interval_count).

The `%` sign indicating a directive may be immediately followed by a sign modifier, `+`. If no sign modifier is specified, the field will be displayed as unsigned. Otherwise, the field will be formatted with a plus sign (`+`) for positive or zero values and a minus sign (`-`) for negative values.

The `%` sign indicating a directive may be immediately followed by a padding modifier:

* `0` - zero-padding
Expand Down
170 changes: 104 additions & 66 deletions src/locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,24 @@ export default function(locale) {
while (++i < n) {
if (specifier.charCodeAt(i) === 37) {
string.push(specifier.slice(j, i));
if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
else pad = c === "e" ? " " : "0";
if (format = formats[c]) c = format(date, pad);
switch (c = specifier.charAt(++i)) {
case "+": {
switch (c = specifier.charAt(++i)) {
case "-": pad = padNoneSigned; break;
case "_": pad = padSpaceSigned; break;
case "0": pad = padZeroSigned; break;
case "e": pad = padSpaceSigned; --i; break;
default: pad = padZeroSigned; --i; break;
}
break;
}
case "-": pad = padNone; break;
case "_": pad = padSpace; break;
case "0": pad = padZero; break;
case "e": pad = padSpace; --i; break;
default: pad = padZero; --i; break;
}
if (format = formats[c = specifier.charAt(++i)]) c = format(date, pad);
string.push(c);
j = i + 1;
}
Expand Down Expand Up @@ -199,8 +214,10 @@ export default function(locale) {
if (j >= m) return -1;
c = specifier.charCodeAt(i++);
if (c === 37) {
c = specifier.charAt(i++);
parse = parses[c in pads ? specifier.charAt(i++) : c];
switch (c = specifier.charAt(i++)) {
case "-": case "_": case "0": c = specifier.charAt(i++); break;
}
parse = parses[c];
if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
} else if (c != string.charCodeAt(j++)) {
return -1;
Expand Down Expand Up @@ -303,16 +320,37 @@ export default function(locale) {
};
};

var pads = {"-": "", "_": " ", "0": "0"},
numberRe = /^\s*\d+/, // note: ignores next directive
var numberRe = /^\s*\d+/, // note: ignores next directive
// TODO signedNumberRe = /^\s*[+-]\d+/,
percentRe = /^%/,
requoteRe = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;

function pad(value, fill, width) {
var sign = value < 0 ? "-" : "",
string = (sign ? -value : value) + "",
length = string.length;
return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
function padNone(value) {
return Math.abs(value) + "";
}

function padZero(value, width) {
var length = (value = Math.abs(value) + "").length;
return length < width ? new Array(width - length + 1).join("0") + value : value;
}

function padSpace(value, width) {
var length = (value = Math.abs(value) + "").length;
return length < width ? new Array(width - length + 1).join(" ") + value : value;
}

function padNoneSigned(value) {
return (value < 0 ? "-" : "+") + Math.abs(value);
}

function padZeroSigned(value, width) {
var sign = value < 0 ? "-" : "+", length = (value = Math.abs(value) + "").length;
return sign + (length < width ? new Array(width - length + 1).join("0") + value : value);
}

function padSpaceSigned(value, width) {
var sign = value < 0 ? "-" : "+", length = (value = Math.abs(value) + "").length;
return (length < width ? new Array(width - length + 1).join(" ") + sign + value : sign + value);
}

function requote(s) {
Expand Down Expand Up @@ -399,115 +437,115 @@ function parseLiteralPercent(d, string, i) {
return n ? i + n[0].length : -1;
}

function formatDayOfMonth(d, p) {
return pad(d.getDate(), p, 2);
function formatDayOfMonth(d, pad) {
return pad(d.getDate(), 2);
}

function formatHour24(d, p) {
return pad(d.getHours(), p, 2);
function formatHour24(d, pad) {
return pad(d.getHours(), 2);
}

function formatHour12(d, p) {
return pad(d.getHours() % 12 || 12, p, 2);
function formatHour12(d, pad) {
return pad(d.getHours() % 12 || 12, 2);
}

function formatDayOfYear(d, p) {
return pad(1 + day.count(year(d), d), p, 3);
function formatDayOfYear(d, pad) {
return pad(1 + day.count(year(d), d), 3);
}

function formatMilliseconds(d, p) {
return pad(d.getMilliseconds(), p, 3);
function formatMilliseconds(d, pad) {
return pad(d.getMilliseconds(), 3);
}

function formatMonthNumber(d, p) {
return pad(d.getMonth() + 1, p, 2);
function formatMonthNumber(d, pad) {
return pad(d.getMonth() + 1, 2);
}

function formatMinutes(d, p) {
return pad(d.getMinutes(), p, 2);
function formatMinutes(d, pad) {
return pad(d.getMinutes(), 2);
}

function formatSeconds(d, p) {
return pad(d.getSeconds(), p, 2);
function formatSeconds(d, pad) {
return pad(d.getSeconds(), 2);
}

function formatWeekNumberSunday(d, p) {
return pad(sunday.count(year(d), d), p, 2);
function formatWeekNumberSunday(d, pad) {
return pad(sunday.count(year(d), d), 2);
}

function formatWeekdayNumber(d) {
return d.getDay();
function formatWeekdayNumber(d, pad) {
return pad(d.getDay(), 1);
}

function formatWeekNumberMonday(d, p) {
return pad(monday.count(year(d), d), p, 2);
function formatWeekNumberMonday(d, pad) {
return pad(monday.count(year(d), d), 2);
}

function formatYear(d, p) {
return pad(Math.abs(d.getFullYear() % 100), p, 2);
function formatYear(d, pad) {
return pad(d.getFullYear() % 100, 2);
}

function formatFullYear(d, p) {
return pad(Math.abs(d.getFullYear() % 10000), p, 4);
function formatFullYear(d, pad) {
return pad(d.getFullYear() % 10000, 4);
}

function formatZone(d) {
var z = d.getTimezoneOffset();
return (z > 0 ? "-" : (z *= -1, "+"))
+ pad(z / 60 | 0, "0", 2)
+ pad(z % 60, "0", 2);
+ padZero(z / 60 | 0, 2)
+ padZero(z % 60, 2);
}

function formatUTCDayOfMonth(d, p) {
return pad(d.getUTCDate(), p, 2);
function formatUTCDayOfMonth(d, pad) {
return pad(d.getUTCDate(), 2);
}

function formatUTCHour24(d, p) {
return pad(d.getUTCHours(), p, 2);
function formatUTCHour24(d, pad) {
return pad(d.getUTCHours(), 2);
}

function formatUTCHour12(d, p) {
return pad(d.getUTCHours() % 12 || 12, p, 2);
function formatUTCHour12(d, pad) {
return pad(d.getUTCHours() % 12 || 12, 2);
}

function formatUTCDayOfYear(d, p) {
return pad(1 + utcDay.count(utcYear(d), d), p, 3);
function formatUTCDayOfYear(d, pad) {
return pad(1 + utcDay.count(utcYear(d), d), 3);
}

function formatUTCMilliseconds(d, p) {
return pad(d.getUTCMilliseconds(), p, 3);
function formatUTCMilliseconds(d, pad) {
return pad(d.getUTCMilliseconds(), 3);
}

function formatUTCMonthNumber(d, p) {
return pad(d.getUTCMonth() + 1, p, 2);
function formatUTCMonthNumber(d, pad) {
return pad(d.getUTCMonth() + 1, 2);
}

function formatUTCMinutes(d, p) {
return pad(d.getUTCMinutes(), p, 2);
function formatUTCMinutes(d, pad) {
return pad(d.getUTCMinutes(), 2);
}

function formatUTCSeconds(d, p) {
return pad(d.getUTCSeconds(), p, 2);
function formatUTCSeconds(d, pad) {
return pad(d.getUTCSeconds(), 2);
}

function formatUTCWeekNumberSunday(d, p) {
return pad(utcSunday.count(utcYear(d), d), p, 2);
function formatUTCWeekNumberSunday(d, pad) {
return pad(utcSunday.count(utcYear(d), d), 2);
}

function formatUTCWeekdayNumber(d) {
return d.getUTCDay();
function formatUTCWeekdayNumber(d, pad) {
return pad(d.getUTCDay(), 1);
}

function formatUTCWeekNumberMonday(d, p) {
return pad(utcMonday.count(utcYear(d), d), p, 2);
function formatUTCWeekNumberMonday(d, pad) {
return pad(utcMonday.count(utcYear(d), d), 2);
}

function formatUTCYear(d, p) {
return pad(Math.abs(d.getUTCFullYear() % 100), p, 2);
function formatUTCYear(d, pad) {
return pad(d.getUTCFullYear() % 100, 2);
}

function formatUTCFullYear(d, p) {
return pad(Math.abs(d.getUTCFullYear() % 10000), p, 4);
function formatUTCFullYear(d, pad) {
return pad(d.getUTCFullYear() % 10000, 4);
}

function formatUTCZone() {
Expand Down
11 changes: 11 additions & 0 deletions test/format-parse-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ tape("format(\"%m/%d/%y\").parse(date) parses month, date and two-digit year", f
test.end();
});

// TODO
// tape("format(\"%m/%d/%+y\").parse(date) parses month, date and signed two-digit year", function(test) {
// var p = timeFormat.format("%m/%d/%y").parse;
// test.deepEqual(p("02/03/+69"), date.local(1969, 1, 3));
// test.deepEqual(p("01/01/+90"), date.local(1990, 0, 1));
// test.deepEqual(p("02/03/+91"), date.local(1991, 1, 3));
// test.deepEqual(p("02/03/-68"), date.local(-68, 1, 3));
// test.equal(p("03/10/10"), null);
// test.end();
// });

tape("format(\"%x\").parse(date) parses locale date", function(test) {
var p = timeFormat.format("%x").parse;
test.deepEqual(p("01/01/1990"), date.local(1990, 0, 1));
Expand Down
36 changes: 36 additions & 0 deletions test/format-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,42 @@ tape("format(\"%Y\")(date) formats zero-padded four-digit years", function(test)
test.end();
});

tape("format(\"%+y\")(date) formats zero-padded signed two-digit years", function(test) {
var f = timeFormat.format("%+y");
test.equal(f(date.local(+1990, 0, 1)), "+90");
test.equal(f(date.local(+2002, 0, 1)), "+02");
test.equal(f(date.local(-0002, 0, 1)), "-02");
test.end();
});

tape("format(\"%+Y\")(date) formats zero-padded signed four-digit years", function(test) {
var f = timeFormat.format("%+Y");
test.equal(f(date.local( 123, 0, 1)), "+0123");
test.equal(f(date.local( 1990, 0, 1)), "+1990");
test.equal(f(date.local( 2002, 0, 1)), "+2002");
test.equal(f(date.local(10002, 0, 1)), "+0002");
test.equal(f(date.local( -2, 0, 1)), "-0002");
test.end();
});

tape("format(\"%+_y\")(date) formats space-padded signed two-digit years", function(test) {
var f = timeFormat.format("%+_y");
test.equal(f(date.local(+1990, 0, 1)), "+90");
test.equal(f(date.local(+2002, 0, 1)), " +2");
test.equal(f(date.local(-0002, 0, 1)), " -2");
test.end();
});

tape("format(\"%+_Y\")(date) formats space-padded signed four-digit years", function(test) {
var f = timeFormat.format("%+_Y");
test.equal(f(date.local( 123, 0, 1)), " +123");
test.equal(f(date.local( 1990, 0, 1)), "+1990");
test.equal(f(date.local( 2002, 0, 1)), "+2002");
test.equal(f(date.local(10002, 0, 1)), " +2");
test.equal(f(date.local( -2, 0, 1)), " -2");
test.end();
});

tape("format(\"%Z\")(date) formats time zones", function(test) {
var f = timeFormat.format("%Z");
test.equal(f(date.local(1990, 0, 1)), "-0800");
Expand Down
36 changes: 36 additions & 0 deletions test/utcFormat-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,42 @@ tape("utcFormat(\"%Y\")(date) formats zero-padded four-digit years", function(te
test.end();
});

tape("utcFormat(\"%+y\")(date) formats zero-padded signed two-digit years", function(test) {
var f = timeFormat.utcFormat("%+y");
test.equal(f(date.utc(+1990, 0, 1)), "+90");
test.equal(f(date.utc(+2002, 0, 1)), "+02");
test.equal(f(date.utc(-0002, 0, 1)), "-02");
test.end();
});

tape("utcFormat(\"%+Y\")(date) formats zero-padded signed four-digit years", function(test) {
var f = timeFormat.utcFormat("%+Y");
test.equal(f(date.utc( 123, 0, 1)), "+0123");
test.equal(f(date.utc( 1990, 0, 1)), "+1990");
test.equal(f(date.utc( 2002, 0, 1)), "+2002");
test.equal(f(date.utc(10002, 0, 1)), "+0002");
test.equal(f(date.utc( -2, 0, 1)), "-0002");
test.end();
});

tape("utcFormat(\"%+_y\")(date) formats space-padded signed two-digit years", function(test) {
var f = timeFormat.utcFormat("%+_y");
test.equal(f(date.utc(+1990, 0, 1)), "+90");
test.equal(f(date.utc(+2002, 0, 1)), " +2");
test.equal(f(date.utc(-0002, 0, 1)), " -2");
test.end();
});

tape("utcFormat(\"%+_Y\")(date) formats space-padded signed four-digit years", function(test) {
var f = timeFormat.utcFormat("%+_Y");
test.equal(f(date.utc( 123, 0, 1)), " +123");
test.equal(f(date.utc( 1990, 0, 1)), "+1990");
test.equal(f(date.utc( 2002, 0, 1)), "+2002");
test.equal(f(date.utc(10002, 0, 1)), " +2");
test.equal(f(date.utc( -2, 0, 1)), " -2");
test.end();
});

tape("utcFormat(\"%Z\")(date) formats time zones", function(test) {
var f = timeFormat.utcFormat("%Z");
test.equal(f(date.utc(1990, 0, 1)), "+0000");
Expand Down

0 comments on commit 370362c

Please sign in to comment.