Skip to content

Commit 3e99976

Browse files
authored
fix local time (#1887)
1 parent 32cd301 commit 3e99976

File tree

5 files changed

+88
-14
lines changed

5 files changed

+88
-14
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"scripts": {
3030
"test": "yarn test:mocha && yarn test:tsc && yarn test:lint && yarn test:prettier",
3131
"test:coverage": "npx c8 yarn test:mocha",
32-
"test:mocha": "mkdir -p test/output && mocha 'test/**/*-test.*' 'test/plot.js'",
32+
"test:mocha": "mkdir -p test/output && TZ=America/Los_Angeles mocha 'test/**/*-test.*' 'test/plot.js'",
3333
"test:lint": "eslint src test",
3434
"test:prettier": "prettier --check src test",
3535
"test:tsc": "tsc",

src/marks/axis.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ export function inferTickFormat(scale, data, ticks, tickFormat, anchor) {
638638
return typeof tickFormat === "function"
639639
? tickFormat
640640
: tickFormat === undefined && data && isTemporal(data)
641-
? inferTimeFormat(data, anchor) ?? formatDefault
641+
? inferTimeFormat(scale.type, data, anchor) ?? formatDefault
642642
: scale.tickFormat
643643
? scale.tickFormat(typeof ticks === "number" ? ticks : null, tickFormat)
644644
: tickFormat === undefined

src/time.js

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,23 +123,39 @@ for (const [name, interval] of utcIntervals) {
123123
interval[intervalType] = "utc";
124124
}
125125

126+
const utcFormatIntervals = [
127+
["year", utcYear, "utc"],
128+
["month", utcMonth, "utc"],
129+
["day", unixDay, "utc", 6 * durationMonth],
130+
["hour", utcHour, "utc", 3 * durationDay],
131+
["minute", utcMinute, "utc", 6 * durationHour],
132+
["second", utcSecond, "utc", 30 * durationMinute]
133+
];
134+
135+
const timeFormatIntervals = [
136+
["year", timeYear, "time"],
137+
["month", timeMonth, "time"],
138+
["day", timeDay, "time", 6 * durationMonth],
139+
["hour", timeHour, "time", 3 * durationDay],
140+
["minute", timeMinute, "time", 6 * durationHour],
141+
["second", timeSecond, "time", 30 * durationMinute]
142+
];
143+
126144
// An interleaved array of UTC and local time intervals, in descending order
127145
// from largest to smallest, used to determine the most specific standard time
128146
// format for a given array of dates. This is a subset of the tick intervals
129147
// listed above; we only need the breakpoints where the format changes.
130148
const formatIntervals = [
131-
["year", utcYear, "utc"],
132-
["year", timeYear, "time"],
133-
["month", utcMonth, "utc"],
134-
["month", timeMonth, "time"],
135-
["day", unixDay, "utc", 6 * durationMonth],
136-
["day", timeDay, "time", 6 * durationMonth],
149+
utcFormatIntervals[0],
150+
timeFormatIntervals[0],
151+
utcFormatIntervals[1],
152+
timeFormatIntervals[1],
153+
utcFormatIntervals[2],
154+
timeFormatIntervals[2],
137155
// Below day, local time typically has an hourly offset from UTC and hence the
138156
// two are aligned and indistinguishable; therefore, we only consider UTC, and
139157
// we don’t consider these if the domain only has a single value.
140-
["hour", utcHour, "utc", 3 * durationDay],
141-
["minute", utcMinute, "utc", 6 * durationHour],
142-
["second", utcSecond, "utc", 30 * durationMinute]
158+
...utcFormatIntervals.slice(3)
143159
];
144160

145161
function parseInterval(input, intervals, type) {
@@ -238,16 +254,20 @@ function getTimeTemplate(anchor) {
238254
: (f1, f2) => `${f1}\n${f2}`;
239255
}
240256

257+
function getFormatIntervals(type) {
258+
return type === "time" ? timeFormatIntervals : type === "utc" ? utcFormatIntervals : formatIntervals;
259+
}
260+
241261
// Given an array of dates, returns the largest compatible standard time
242262
// interval. If no standard interval is compatible (other than milliseconds,
243263
// which is universally compatible), returns undefined.
244-
export function inferTimeFormat(dates, anchor) {
264+
export function inferTimeFormat(type, dates, anchor) {
245265
const step = max(pairs(dates, (a, b) => Math.abs(b - a))); // maybe undefined!
246266
if (step < 1000) return formatTimeInterval("millisecond", "utc", anchor);
247-
for (const [name, interval, type, maxStep] of formatIntervals) {
267+
for (const [name, interval, intervalType, maxStep] of getFormatIntervals(type)) {
248268
if (step > maxStep) break; // e.g., 52 weeks
249269
if (name === "hour" && !step) break; // e.g., domain with a single date
250-
if (dates.every((d) => interval.floor(d) >= d)) return formatTimeInterval(name, type, anchor);
270+
if (dates.every((d) => interval.floor(d) >= d)) return formatTimeInterval(name, intervalType, anchor);
251271
}
252272
}
253273

test/output/timeAxisLocal.svg

Lines changed: 43 additions & 0 deletions
Loading

test/plots/time-axis.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,14 @@ export async function warnTimeAxisOrdinalExplicitIncompatibleTicks() {
148148
marks: [Plot.barY(aapl, Plot.groupX({y: "median", title: "min"}, {title: "Date", x: "Date", y: "Close"}))]
149149
});
150150
}
151+
152+
export async function timeAxisLocal() {
153+
const dates = [
154+
"2023-09-30T15:05:48.452Z",
155+
"2023-09-30T16:05:48.452Z",
156+
"2023-09-30T17:05:48.452Z",
157+
"2023-09-30T18:05:48.452Z",
158+
"2023-09-30T19:05:48.452Z"
159+
].map((d) => new Date(d));
160+
return Plot.dotX(dates).plot({x: {type: "time"}});
161+
}

0 commit comments

Comments
 (0)