Skip to content

Commit 41371ad

Browse files
committed
Fixes d3#576: leap year bug in d3.time.format.
We need to set the date fields from year down, rather than in arbitrary order.
1 parent e46b161 commit 41371ad

21 files changed

Lines changed: 180 additions & 235 deletions

d3.v2.js

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8640,16 +8640,18 @@ d3_time_utc.prototype = {
86408640
getTime: function() { return this._.getTime(); },
86418641
getTimezoneOffset: function() { return 0; },
86428642
valueOf: function() { return this._.valueOf(); },
8643-
setDate: function(x) { this._.setUTCDate(x); },
8644-
setDay: function(x) { this._.setUTCDay(x); },
8645-
setFullYear: function(x) { this._.setUTCFullYear(x); },
8646-
setHours: function(x) { this._.setUTCHours(x); },
8647-
setMilliseconds: function(x) { this._.setUTCMilliseconds(x); },
8648-
setMinutes: function(x) { this._.setUTCMinutes(x); },
8649-
setMonth: function(x) { this._.setUTCMonth(x); },
8650-
setSeconds: function(x) { this._.setUTCSeconds(x); },
8651-
setTime: function(x) { this._.setTime(x); }
8652-
};
8643+
setDate: function() { d3_time_prototype.setUTCDate.apply(this._, arguments); },
8644+
setDay: function() { d3_time_prototype.setUTCDay.apply(this._, arguments); },
8645+
setFullYear: function() { d3_time_prototype.setUTCFullYear.apply(this._, arguments); },
8646+
setHours: function() { d3_time_prototype.setUTCHours.apply(this._, arguments); },
8647+
setMilliseconds: function() { d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); },
8648+
setMinutes: function() { d3_time_prototype.setUTCMinutes.apply(this._, arguments); },
8649+
setMonth: function() { d3_time_prototype.setUTCMonth.apply(this._, arguments); },
8650+
setSeconds: function() { d3_time_prototype.setUTCSeconds.apply(this._, arguments); },
8651+
setTime: function() { d3_time_prototype.setTime.apply(this._, arguments); }
8652+
};
8653+
8654+
var d3_time_prototype = Date.prototype;
86538655
d3.time.format = function(template) {
86548656
var n = template.length;
86558657

@@ -8673,15 +8675,16 @@ d3.time.format = function(template) {
86738675
}
86748676

86758677
format.parse = function(string) {
8676-
var date = new d3_time(1900, 0, 1),
8677-
i = d3_time_parse(date, template, string, 0);
8678+
var d = {y: 1900, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0},
8679+
i = d3_time_parse(d, template, string, 0);
86788680
if (i != string.length) return null;
8679-
if (date.hour12) {
8680-
var hours = date.getHours() % 12;
8681-
date.setHours(date.hour12pm ? hours + 12 : hours);
8682-
}
8683-
delete date.hour12;
8684-
delete date.hour12pm;
8681+
8682+
// The am-pm flag is 0 for AM, and 1 for PM.
8683+
if ("p" in d) d.H = d.H % 12 + d.p * 12;
8684+
8685+
var date = new d3_time();
8686+
date.setFullYear(d.y, d.m, d.d);
8687+
date.setHours(d.H, d.M, d.S, d.L);
86858688
return date;
86868689
};
86878690

@@ -8752,7 +8755,7 @@ var d3_time_parsers = {
87528755
d: d3_time_parseDay,
87538756
e: d3_time_parseDay,
87548757
H: d3_time_parseHour24,
8755-
I: d3_time_parseHour12,
8758+
I: d3_time_parseHour24,
87568759
// j: function(d, s, i) { /*TODO day of year [001,366] */ return i; },
87578760
L: d3_time_parseMilliseconds,
87588761
m: d3_time_parseMonthNumber,
@@ -8789,7 +8792,7 @@ var d3_time_weekdayAbbrevRe = /^(?:sun|mon|tue|wed|thu|fri|sat)/i,
87898792

87908793
function d3_time_parseMonthAbbrev(date, string, i) {
87918794
var n = d3_time_monthAbbrevLookup.get(string.substring(i, i += 3).toLowerCase());
8792-
return n == null ? -1 : (date.setMonth(n), i);
8795+
return n == null ? -1 : (date.m = n, i);
87938796
}
87948797

87958798
var d3_time_monthAbbrevLookup = d3.map({
@@ -8810,7 +8813,7 @@ var d3_time_monthAbbrevLookup = d3.map({
88108813
function d3_time_parseMonth(date, string, i) {
88118814
d3_time_monthRe.lastIndex = 0;
88128815
var n = d3_time_monthRe.exec(string.substring(i, i + 12));
8813-
return n ? (date.setMonth(d3_time_monthLookup.get(n[0].toLowerCase())), i += n[0].length) : -1;
8816+
return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i += n[0].length) : -1;
88148817
}
88158818

88168819
var d3_time_monthRe = /^(?:January|February|March|April|May|June|July|August|September|October|November|December)/ig;
@@ -8860,13 +8863,13 @@ function d3_time_parseLocaleTime(date, string, i) {
88608863
function d3_time_parseFullYear(date, string, i) {
88618864
d3_time_numberRe.lastIndex = 0;
88628865
var n = d3_time_numberRe.exec(string.substring(i, i + 4));
8863-
return n ? (date.setFullYear(n[0]), i += n[0].length) : -1;
8866+
return n ? (date.y = +n[0], i += n[0].length) : -1;
88648867
}
88658868

88668869
function d3_time_parseYear(date, string, i) {
88678870
d3_time_numberRe.lastIndex = 0;
88688871
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
8869-
return n ? (date.setFullYear(d3_time_century() + +n[0]), i += n[0].length) : -1;
8872+
return n ? (date.y = d3_time_century() + +n[0], i += n[0].length) : -1;
88708873
}
88718874

88728875
function d3_time_century() {
@@ -8876,52 +8879,46 @@ function d3_time_century() {
88768879
function d3_time_parseMonthNumber(date, string, i) {
88778880
d3_time_numberRe.lastIndex = 0;
88788881
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
8879-
return n ? (date.setMonth(n[0] - 1), i += n[0].length) : -1;
8882+
return n ? (date.m = n[0] - 1, i += n[0].length) : -1;
88808883
}
88818884

88828885
function d3_time_parseDay(date, string, i) {
88838886
d3_time_numberRe.lastIndex = 0;
88848887
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
8885-
return n ? (date.setDate(+n[0]), i += n[0].length) : -1;
8888+
return n ? (date.d = +n[0], i += n[0].length) : -1;
88868889
}
88878890

8888-
// Note: we don't validate that the hour is in the range [0,23].
8891+
// Note: we don't validate that the hour is in the range [0,23] or [1,12].
88898892
function d3_time_parseHour24(date, string, i) {
88908893
d3_time_numberRe.lastIndex = 0;
88918894
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
8892-
return n ? (date.setHours(+n[0]), i += n[0].length) : -1;
8893-
}
8894-
8895-
// Note: we don't validate that the hour is in the range [1,12].
8896-
function d3_time_parseHour12(date, string, i) {
8897-
date.hour12 = true;
8898-
return d3_time_parseHour24(date, string, i);
8895+
return n ? (date.H = +n[0], i += n[0].length) : -1;
88998896
}
89008897

89018898
function d3_time_parseMinutes(date, string, i) {
89028899
d3_time_numberRe.lastIndex = 0;
89038900
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
8904-
return n ? (date.setMinutes(+n[0]), i += n[0].length) : -1;
8901+
return n ? (date.M = +n[0], i += n[0].length) : -1;
89058902
}
89068903

89078904
function d3_time_parseSeconds(date, string, i) {
89088905
d3_time_numberRe.lastIndex = 0;
89098906
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
8910-
return n ? (date.setSeconds(+n[0]), i += n[0].length) : -1;
8907+
return n ? (date.S = +n[0], i += n[0].length) : -1;
89118908
}
89128909

89138910
function d3_time_parseMilliseconds(date, string, i) {
89148911
d3_time_numberRe.lastIndex = 0;
89158912
var n = d3_time_numberRe.exec(string.substring(i, i + 3));
8916-
return n ? (date.setMilliseconds(+n[0]), i += n[0].length) : -1;
8913+
return n ? (date.L = +n[0], i += n[0].length) : -1;
89178914
}
89188915

89198916
// Note: we don't look at the next directive.
89208917
var d3_time_numberRe = /\s*\d+/;
89218918

89228919
function d3_time_parseAmPm(date, string, i) {
89238920
var n = d3_time_amPmLookup.get(string.substring(i, i += 2).toLowerCase());
8924-
return n == null ? -1 : (date.hour12pm = n, i);
8921+
return n == null ? -1 : (date.p = n, i);
89258922
}
89268923

89278924
var d3_time_amPmLookup = d3.map({

d3.v2.min.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/time/format.js

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ d3.time.format = function(template) {
2121
}
2222

2323
format.parse = function(string) {
24-
var date = new d3_time(1900, 0, 1),
25-
i = d3_time_parse(date, template, string, 0);
24+
var d = {y: 1900, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0},
25+
i = d3_time_parse(d, template, string, 0);
2626
if (i != string.length) return null;
27-
if (date.hour12) {
28-
var hours = date.getHours() % 12;
29-
date.setHours(date.hour12pm ? hours + 12 : hours);
30-
}
31-
delete date.hour12;
32-
delete date.hour12pm;
27+
28+
// The am-pm flag is 0 for AM, and 1 for PM.
29+
if ("p" in d) d.H = d.H % 12 + d.p * 12;
30+
31+
var date = new d3_time();
32+
date.setFullYear(d.y, d.m, d.d);
33+
date.setHours(d.H, d.M, d.S, d.L);
3334
return date;
3435
};
3536

@@ -100,7 +101,7 @@ var d3_time_parsers = {
100101
d: d3_time_parseDay,
101102
e: d3_time_parseDay,
102103
H: d3_time_parseHour24,
103-
I: d3_time_parseHour12,
104+
I: d3_time_parseHour24,
104105
// j: function(d, s, i) { /*TODO day of year [001,366] */ return i; },
105106
L: d3_time_parseMilliseconds,
106107
m: d3_time_parseMonthNumber,
@@ -137,7 +138,7 @@ var d3_time_weekdayAbbrevRe = /^(?:sun|mon|tue|wed|thu|fri|sat)/i,
137138

138139
function d3_time_parseMonthAbbrev(date, string, i) {
139140
var n = d3_time_monthAbbrevLookup.get(string.substring(i, i += 3).toLowerCase());
140-
return n == null ? -1 : (date.setMonth(n), i);
141+
return n == null ? -1 : (date.m = n, i);
141142
}
142143

143144
var d3_time_monthAbbrevLookup = d3.map({
@@ -158,7 +159,7 @@ var d3_time_monthAbbrevLookup = d3.map({
158159
function d3_time_parseMonth(date, string, i) {
159160
d3_time_monthRe.lastIndex = 0;
160161
var n = d3_time_monthRe.exec(string.substring(i, i + 12));
161-
return n ? (date.setMonth(d3_time_monthLookup.get(n[0].toLowerCase())), i += n[0].length) : -1;
162+
return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i += n[0].length) : -1;
162163
}
163164

164165
var d3_time_monthRe = /^(?:January|February|March|April|May|June|July|August|September|October|November|December)/ig;
@@ -208,13 +209,13 @@ function d3_time_parseLocaleTime(date, string, i) {
208209
function d3_time_parseFullYear(date, string, i) {
209210
d3_time_numberRe.lastIndex = 0;
210211
var n = d3_time_numberRe.exec(string.substring(i, i + 4));
211-
return n ? (date.setFullYear(n[0]), i += n[0].length) : -1;
212+
return n ? (date.y = +n[0], i += n[0].length) : -1;
212213
}
213214

214215
function d3_time_parseYear(date, string, i) {
215216
d3_time_numberRe.lastIndex = 0;
216217
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
217-
return n ? (date.setFullYear(d3_time_century() + +n[0]), i += n[0].length) : -1;
218+
return n ? (date.y = d3_time_century() + +n[0], i += n[0].length) : -1;
218219
}
219220

220221
function d3_time_century() {
@@ -224,52 +225,46 @@ function d3_time_century() {
224225
function d3_time_parseMonthNumber(date, string, i) {
225226
d3_time_numberRe.lastIndex = 0;
226227
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
227-
return n ? (date.setMonth(n[0] - 1), i += n[0].length) : -1;
228+
return n ? (date.m = n[0] - 1, i += n[0].length) : -1;
228229
}
229230

230231
function d3_time_parseDay(date, string, i) {
231232
d3_time_numberRe.lastIndex = 0;
232233
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
233-
return n ? (date.setDate(+n[0]), i += n[0].length) : -1;
234+
return n ? (date.d = +n[0], i += n[0].length) : -1;
234235
}
235236

236-
// Note: we don't validate that the hour is in the range [0,23].
237+
// Note: we don't validate that the hour is in the range [0,23] or [1,12].
237238
function d3_time_parseHour24(date, string, i) {
238239
d3_time_numberRe.lastIndex = 0;
239240
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
240-
return n ? (date.setHours(+n[0]), i += n[0].length) : -1;
241-
}
242-
243-
// Note: we don't validate that the hour is in the range [1,12].
244-
function d3_time_parseHour12(date, string, i) {
245-
date.hour12 = true;
246-
return d3_time_parseHour24(date, string, i);
241+
return n ? (date.H = +n[0], i += n[0].length) : -1;
247242
}
248243

249244
function d3_time_parseMinutes(date, string, i) {
250245
d3_time_numberRe.lastIndex = 0;
251246
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
252-
return n ? (date.setMinutes(+n[0]), i += n[0].length) : -1;
247+
return n ? (date.M = +n[0], i += n[0].length) : -1;
253248
}
254249

255250
function d3_time_parseSeconds(date, string, i) {
256251
d3_time_numberRe.lastIndex = 0;
257252
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
258-
return n ? (date.setSeconds(+n[0]), i += n[0].length) : -1;
253+
return n ? (date.S = +n[0], i += n[0].length) : -1;
259254
}
260255

261256
function d3_time_parseMilliseconds(date, string, i) {
262257
d3_time_numberRe.lastIndex = 0;
263258
var n = d3_time_numberRe.exec(string.substring(i, i + 3));
264-
return n ? (date.setMilliseconds(+n[0]), i += n[0].length) : -1;
259+
return n ? (date.L = +n[0], i += n[0].length) : -1;
265260
}
266261

267262
// Note: we don't look at the next directive.
268263
var d3_time_numberRe = /\s*\d+/;
269264

270265
function d3_time_parseAmPm(date, string, i) {
271266
var n = d3_time_amPmLookup.get(string.substring(i, i += 2).toLowerCase());
272-
return n == null ? -1 : (date.hour12pm = n, i);
267+
return n == null ? -1 : (date.p = n, i);
273268
}
274269

275270
var d3_time_amPmLookup = d3.map({

src/time/time.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ d3_time_utc.prototype = {
2020
getTime: function() { return this._.getTime(); },
2121
getTimezoneOffset: function() { return 0; },
2222
valueOf: function() { return this._.valueOf(); },
23-
setDate: function(x) { this._.setUTCDate(x); },
24-
setDay: function(x) { this._.setUTCDay(x); },
25-
setFullYear: function(x) { this._.setUTCFullYear(x); },
26-
setHours: function(x) { this._.setUTCHours(x); },
27-
setMilliseconds: function(x) { this._.setUTCMilliseconds(x); },
28-
setMinutes: function(x) { this._.setUTCMinutes(x); },
29-
setMonth: function(x) { this._.setUTCMonth(x); },
30-
setSeconds: function(x) { this._.setUTCSeconds(x); },
31-
setTime: function(x) { this._.setTime(x); }
23+
setDate: function() { d3_time_prototype.setUTCDate.apply(this._, arguments); },
24+
setDay: function() { d3_time_prototype.setUTCDay.apply(this._, arguments); },
25+
setFullYear: function() { d3_time_prototype.setUTCFullYear.apply(this._, arguments); },
26+
setHours: function() { d3_time_prototype.setUTCHours.apply(this._, arguments); },
27+
setMilliseconds: function() { d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); },
28+
setMinutes: function() { d3_time_prototype.setUTCMinutes.apply(this._, arguments); },
29+
setMonth: function() { d3_time_prototype.setUTCMonth.apply(this._, arguments); },
30+
setSeconds: function() { d3_time_prototype.setUTCSeconds.apply(this._, arguments); },
31+
setTime: function() { d3_time_prototype.setTime.apply(this._, arguments); }
3232
};
33+
34+
var d3_time_prototype = Date.prototype;

test/time/day-test.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
require("../env");
22

33
var vows = require("vows"),
4-
assert = require("assert");
4+
assert = require("assert"),
5+
time = require("./time"),
6+
local = time.local,
7+
utc = time.utc;
58

69
var suite = vows.describe("d3.time.day");
710

@@ -168,12 +171,4 @@ suite.addBatch({
168171
}
169172
});
170173

171-
function local(year, month, day, hours, minutes, seconds) {
172-
return new Date(year, month, day, hours || 00, minutes || 00, seconds || 00);
173-
}
174-
175-
function utc(year, month, day, hours, minutes, seconds) {
176-
return new Date(Date.UTC(year, month, day, hours || 00, minutes || 00, seconds || 00));
177-
}
178-
179174
suite.export(module);

test/time/days-test.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
require("../env");
22

33
var vows = require("vows"),
4-
assert = require("assert");
4+
assert = require("assert"),
5+
time = require("./time"),
6+
local = time.local,
7+
utc = time.utc;
58

69
var suite = vows.describe("d3.time.days");
710

@@ -92,12 +95,4 @@ suite.addBatch({
9295
}
9396
});
9497

95-
function local(year, month, day, hours, minutes, seconds) {
96-
return new Date(year, month, day, hours || 0, minutes || 0, seconds || 0);
97-
}
98-
99-
function utc(year, month, day, hours, minutes, seconds) {
100-
return new Date(Date.UTC(year, month, day, hours || 0, minutes || 0, seconds || 0));
101-
}
102-
10398
suite.export(module);

test/time/format-test.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
require("../env");
22

33
var vows = require("vows"),
4-
assert = require("assert");
4+
assert = require("assert"),
5+
time = require("./time"),
6+
local = time.local,
7+
utc = time.utc;
58

69
var suite = vows.describe("d3.time.format");
710

@@ -463,12 +466,4 @@ suite.addBatch({
463466
}
464467
});
465468

466-
function local(year, month, day, hours, minutes, seconds, milliseconds) {
467-
return new Date(year, month, day, hours || 0, minutes || 0, seconds || 0, milliseconds || 0);
468-
}
469-
470-
function utc(year, month, day, hours, minutes, seconds, milliseconds) {
471-
return new Date(Date.UTC(year, month, day, hours || 0, minutes || 0, seconds || 0, milliseconds || 0));
472-
}
473-
474469
suite.export(module);

0 commit comments

Comments
 (0)