Skip to content

Commit 7e2cf5a

Browse files
committed
Support parsing timezone offsets.
Note that the resulting Date object can only be in the local timezone (or UTC), due to the limitations of JavaScript Dates. Fixes d3#1494.
1 parent fc5a586 commit 7e2cf5a

5 files changed

Lines changed: 39 additions & 14 deletions

File tree

d3.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8513,17 +8513,18 @@ d3 = function() {
85138513
H: 0,
85148514
M: 0,
85158515
S: 0,
8516-
L: 0
8516+
L: 0,
8517+
Z: null
85178518
}, i = d3_time_parse(d, template, string, 0);
85188519
if (i != string.length) return null;
85198520
if ("p" in d) d.H = d.H % 12 + d.p * 12;
8520-
var date = new d3_date();
8521+
var utcZone = d.Z != null && d3_date !== d3_date_utc, date = new (utcZone ? d3_date_utc : d3_date)();
85218522
if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) {
85228523
date.setFullYear(d.y, 0, 1);
85238524
date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
85248525
} else date.setFullYear(d.y, d.m, d.d);
8525-
date.setHours(d.H, d.M, d.S, d.L);
8526-
return date;
8526+
date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L);
8527+
return utcZone ? new d3_date(+date) : date;
85278528
};
85288529
format.toString = function() {
85298530
return template;
@@ -8652,6 +8653,7 @@ d3 = function() {
86528653
X: d3_time_parseLocaleTime,
86538654
y: d3_time_parseYear,
86548655
Y: d3_time_parseFullYear,
8656+
Z: d3_time_parseZone,
86558657
"%": d3_time_parseLiteralPercent
86568658
};
86578659
function d3_time_parseWeekdayAbbrev(date, string, i) {
@@ -8708,6 +8710,10 @@ d3 = function() {
87088710
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
87098711
return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
87108712
}
8713+
function d3_time_parseZone(date, string, i) {
8714+
return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = +string,
8715+
i + 5) : -1;
8716+
}
87118717
function d3_time_expandYear(d) {
87128718
return d + (d > 68 ? 1900 : 2e3);
87138719
}

d3.min.js

Lines changed: 5 additions & 5 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: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,24 @@ function d3_time_format(template) {
3131
}
3232

3333
format.parse = function(string) {
34-
var d = {y: 1900, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0},
34+
var d = {y: 1900, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0, Z: null},
3535
i = d3_time_parse(d, template, string, 0);
3636
if (i != string.length) return null;
3737

3838
// The am-pm flag is 0 for AM, and 1 for PM.
3939
if ("p" in d) d.H = d.H % 12 + d.p * 12;
4040

41-
var date = new d3_date;
41+
var utcZone = d.Z != null && d3_date !== d3_date_utc,
42+
date = new (utcZone ? d3_date_utc : d3_date);
4243
if ("j" in d) date.setFullYear(d.y, 0, d.j);
4344
else if ("w" in d && ("W" in d || "U" in d)) {
4445
date.setFullYear(d.y, 0, 1);
4546
date.setFullYear(d.y, 0, "W" in d
4647
? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7
4748
: d.w + d.U * 7 - (date.getDay() + 6) % 7);
4849
} else date.setFullYear(d.y, d.m, d.d);
49-
date.setHours(d.H, d.M, d.S, d.L);
50-
return date;
50+
date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L);
51+
return utcZone ? new d3_date(+date) : date;
5152
};
5253

5354
format.toString = function() {
@@ -161,7 +162,7 @@ var d3_time_parsers = {
161162
X: d3_time_parseLocaleTime,
162163
y: d3_time_parseYear,
163164
Y: d3_time_parseFullYear,
164-
// Z: function(d, s, i) { /*TODO time zone */ return i; },
165+
Z: d3_time_parseZone,
165166
"%": d3_time_parseLiteralPercent
166167
};
167168

@@ -231,6 +232,12 @@ function d3_time_parseYear(date, string, i) {
231232
return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
232233
}
233234

235+
function d3_time_parseZone(date, string, i) {
236+
return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5))
237+
? (date.Z = +string, i + 5)
238+
: -1;
239+
}
240+
234241
function d3_time_expandYear(d) {
235242
return d + (d > 68 ? 1900 : 2000);
236243
}

test/time/format-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ suite.addBatch({
284284
assert.deepEqual(p("% 02/03/1991"), local(1991, 1, 3));
285285
assert.isNull(p("%% 03/10/2010"));
286286
},
287+
"parses timezone offset": function(format) {
288+
var p = format("%m/%d/%Y %Z").parse;
289+
assert.deepEqual(p("01/02/1990 +0000"), local(1990, 0, 1, 16));
290+
assert.deepEqual(p("01/02/1990 +0100"), local(1990, 0, 1, 17));
291+
assert.deepEqual(p("01/02/1990 -0100"), local(1990, 0, 1, 15));
292+
},
287293
"ignores optional padding modifier, skipping zeroes and spaces": function(format) {
288294
var p = format("%-m/%0d/%_Y").parse;
289295
assert.deepEqual(p("01/ 1/1990"), local(1990, 0, 1));

test/time/format-utc-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,12 @@ suite.addBatch({
211211
assert.deepEqual(p("12:00:00 pm"), utc(1900, 0, 1, 12, 0, 0));
212212
assert.deepEqual(p("12:00:01 pm"), utc(1900, 0, 1, 12, 0, 1));
213213
assert.deepEqual(p("11:59:59 PM"), utc(1900, 0, 1, 23, 59, 59));
214+
},
215+
"parses timezone offset": function(format) {
216+
var p = format("%m/%d/%Y %Z").parse;
217+
assert.deepEqual(p("01/02/1990 +0000"), utc(1990, 0, 2));
218+
assert.deepEqual(p("01/02/1990 +0100"), utc(1990, 0, 2, 1));
219+
assert.deepEqual(p("01/02/1990 -0100"), utc(1990, 0, 1, 23));
214220
}
215221
}
216222
}

0 commit comments

Comments
 (0)