vega-time
Version:
JavaScript date/time utilities for Vega.
308 lines (294 loc) • 9.54 kB
JavaScript
import { array, error, hasOwnProperty, extend, peek, toSet, constant, zero, one, span } from 'vega-util';
import { timeDay, timeWeek, utcDay, utcWeek, timeMonth, utcMonth, timeMillisecond, timeSecond, timeMinute, timeHour, timeYear, utcMillisecond, utcSecond, utcMinute, utcHour, utcYear } from 'd3-time';
import { bisector, tickStep } from 'd3-array';
const YEAR = 'year';
const QUARTER = 'quarter';
const MONTH = 'month';
const WEEK = 'week';
const DATE = 'date';
const DAY = 'day';
const DAYOFYEAR = 'dayofyear';
const HOURS = 'hours';
const MINUTES = 'minutes';
const SECONDS = 'seconds';
const MILLISECONDS = 'milliseconds';
const TIME_UNITS = [YEAR, QUARTER, MONTH, WEEK, DATE, DAY, DAYOFYEAR, HOURS, MINUTES, SECONDS, MILLISECONDS];
const UNITS = TIME_UNITS.reduce((o, u, i) => (o[u] = 1 + i, o), {});
function timeUnits(units) {
const u = array(units).slice(),
m = {};
// check validity
if (!u.length) error('Missing time unit.');
u.forEach(unit => {
if (hasOwnProperty(UNITS, unit)) {
m[unit] = 1;
} else {
error(`Invalid time unit: ${unit}.`);
}
});
const numTypes = (m[WEEK] || m[DAY] ? 1 : 0) + (m[QUARTER] || m[MONTH] || m[DATE] ? 1 : 0) + (m[DAYOFYEAR] ? 1 : 0);
if (numTypes > 1) {
error(`Incompatible time units: ${units}`);
}
// ensure proper sort order
u.sort((a, b) => UNITS[a] - UNITS[b]);
return u;
}
const defaultSpecifiers = {
[YEAR]: '%Y ',
[QUARTER]: 'Q%q ',
[MONTH]: '%b ',
[DATE]: '%d ',
[WEEK]: 'W%U ',
[DAY]: '%a ',
[DAYOFYEAR]: '%j ',
[HOURS]: '%H:00',
[MINUTES]: '00:%M',
[SECONDS]: ':%S',
[MILLISECONDS]: '.%L',
[`${YEAR}-${MONTH}`]: '%Y-%m ',
[`${YEAR}-${MONTH}-${DATE}`]: '%Y-%m-%d ',
[`${HOURS}-${MINUTES}`]: '%H:%M'
};
function timeUnitSpecifier(units, specifiers) {
const s = extend({}, defaultSpecifiers, specifiers),
u = timeUnits(units),
n = u.length;
let fmt = '',
start = 0,
end,
key;
for (start = 0; start < n;) {
for (end = u.length; end > start; --end) {
key = u.slice(start, end).join('-');
if (s[key] != null) {
fmt += s[key];
start = end;
break;
}
}
}
return fmt.trim();
}
const t0 = new Date();
function localYear(y) {
t0.setFullYear(y);
t0.setMonth(0);
t0.setDate(1);
t0.setHours(0, 0, 0, 0);
return t0;
}
function dayofyear(d) {
return localDayOfYear(new Date(d));
}
function week(d) {
return localWeekNum(new Date(d));
}
function localDayOfYear(d) {
return timeDay.count(localYear(d.getFullYear()) - 1, d);
}
function localWeekNum(d) {
return timeWeek.count(localYear(d.getFullYear()) - 1, d);
}
function localFirst(y) {
return localYear(y).getDay();
}
function localDate(y, m, d, H, M, S, L) {
if (0 <= y && y < 100) {
const date = new Date(-1, m, d, H, M, S, L);
date.setFullYear(y);
return date;
}
return new Date(y, m, d, H, M, S, L);
}
function utcdayofyear(d) {
return utcDayOfYear(new Date(d));
}
function utcweek(d) {
return utcWeekNum(new Date(d));
}
function utcDayOfYear(d) {
const y = Date.UTC(d.getUTCFullYear(), 0, 1);
return utcDay.count(y - 1, d);
}
function utcWeekNum(d) {
const y = Date.UTC(d.getUTCFullYear(), 0, 1);
return utcWeek.count(y - 1, d);
}
function utcFirst(y) {
t0.setTime(Date.UTC(y, 0, 1));
return t0.getUTCDay();
}
function utcDate(y, m, d, H, M, S, L) {
if (0 <= y && y < 100) {
const date = new Date(Date.UTC(-1, m, d, H, M, S, L));
date.setUTCFullYear(d.y);
return date;
}
return new Date(Date.UTC(y, m, d, H, M, S, L));
}
function floor(units, step, get, inv, newDate) {
const s = step || 1,
b = peek(units),
_ = (unit, p, key) => {
key = key || unit;
return getUnit(get[key], inv[key], unit === b && s, p);
};
const t = new Date(),
u = toSet(units),
y = u[YEAR] ? _(YEAR) : constant(2012),
m = u[MONTH] ? _(MONTH) : u[QUARTER] ? _(QUARTER) : zero,
d = u[WEEK] && u[DAY] ? _(DAY, 1, WEEK + DAY) : u[WEEK] ? _(WEEK, 1) : u[DAY] ? _(DAY, 1) : u[DATE] ? _(DATE, 1) : u[DAYOFYEAR] ? _(DAYOFYEAR, 1) : one,
H = u[HOURS] ? _(HOURS) : zero,
M = u[MINUTES] ? _(MINUTES) : zero,
S = u[SECONDS] ? _(SECONDS) : zero,
L = u[MILLISECONDS] ? _(MILLISECONDS) : zero;
return function (v) {
t.setTime(+v);
const year = y(t);
return newDate(year, m(t), d(t, year), H(t), M(t), S(t), L(t));
};
}
function getUnit(f, inv, step, phase) {
const u = step <= 1 ? f : phase ? (d, y) => phase + step * Math.floor((f(d, y) - phase) / step) : (d, y) => step * Math.floor(f(d, y) / step);
return inv ? (d, y) => inv(u(d, y), y) : u;
}
// returns the day of the year based on week number, day of week,
// and the day of the week for the first day of the year
function weekday(week, day, firstDay) {
return day + week * 7 - (firstDay + 6) % 7;
}
// -- LOCAL TIME --
const localGet = {
[YEAR]: d => d.getFullYear(),
[QUARTER]: d => Math.floor(d.getMonth() / 3),
[MONTH]: d => d.getMonth(),
[DATE]: d => d.getDate(),
[HOURS]: d => d.getHours(),
[MINUTES]: d => d.getMinutes(),
[SECONDS]: d => d.getSeconds(),
[MILLISECONDS]: d => d.getMilliseconds(),
[DAYOFYEAR]: d => localDayOfYear(d),
[WEEK]: d => localWeekNum(d),
[WEEK + DAY]: (d, y) => weekday(localWeekNum(d), d.getDay(), localFirst(y)),
[DAY]: (d, y) => weekday(1, d.getDay(), localFirst(y))
};
const localInv = {
[QUARTER]: q => 3 * q,
[WEEK]: (w, y) => weekday(w, 0, localFirst(y))
};
function timeFloor(units, step) {
return floor(units, step || 1, localGet, localInv, localDate);
}
// -- UTC TIME --
const utcGet = {
[YEAR]: d => d.getUTCFullYear(),
[QUARTER]: d => Math.floor(d.getUTCMonth() / 3),
[MONTH]: d => d.getUTCMonth(),
[DATE]: d => d.getUTCDate(),
[HOURS]: d => d.getUTCHours(),
[MINUTES]: d => d.getUTCMinutes(),
[SECONDS]: d => d.getUTCSeconds(),
[MILLISECONDS]: d => d.getUTCMilliseconds(),
[DAYOFYEAR]: d => utcDayOfYear(d),
[WEEK]: d => utcWeekNum(d),
[DAY]: (d, y) => weekday(1, d.getUTCDay(), utcFirst(y)),
[WEEK + DAY]: (d, y) => weekday(utcWeekNum(d), d.getUTCDay(), utcFirst(y))
};
const utcInv = {
[QUARTER]: q => 3 * q,
[WEEK]: (w, y) => weekday(w, 0, utcFirst(y))
};
function utcFloor(units, step) {
return floor(units, step || 1, utcGet, utcInv, utcDate);
}
const timeIntervals = {
[YEAR]: timeYear,
[QUARTER]: timeMonth.every(3),
[MONTH]: timeMonth,
[WEEK]: timeWeek,
[DATE]: timeDay,
[DAY]: timeDay,
[DAYOFYEAR]: timeDay,
[HOURS]: timeHour,
[MINUTES]: timeMinute,
[SECONDS]: timeSecond,
[MILLISECONDS]: timeMillisecond
};
const utcIntervals = {
[YEAR]: utcYear,
[QUARTER]: utcMonth.every(3),
[MONTH]: utcMonth,
[WEEK]: utcWeek,
[DATE]: utcDay,
[DAY]: utcDay,
[DAYOFYEAR]: utcDay,
[HOURS]: utcHour,
[MINUTES]: utcMinute,
[SECONDS]: utcSecond,
[MILLISECONDS]: utcMillisecond
};
function timeInterval(unit) {
return timeIntervals[unit];
}
function utcInterval(unit) {
return utcIntervals[unit];
}
function offset(ival, date, step) {
return ival ? ival.offset(date, step) : undefined;
}
function timeOffset(unit, date, step) {
return offset(timeInterval(unit), date, step);
}
function utcOffset(unit, date, step) {
return offset(utcInterval(unit), date, step);
}
function sequence(ival, start, stop, step) {
return ival ? ival.range(start, stop, step) : undefined;
}
function timeSequence(unit, start, stop, step) {
return sequence(timeInterval(unit), start, stop, step);
}
function utcSequence(unit, start, stop, step) {
return sequence(utcInterval(unit), start, stop, step);
}
const durationSecond = 1000,
durationMinute = durationSecond * 60,
durationHour = durationMinute * 60,
durationDay = durationHour * 24,
durationWeek = durationDay * 7,
durationMonth = durationDay * 30,
durationYear = durationDay * 365;
const Milli = [YEAR, MONTH, DATE, HOURS, MINUTES, SECONDS, MILLISECONDS],
Seconds = Milli.slice(0, -1),
Minutes = Seconds.slice(0, -1),
Hours = Minutes.slice(0, -1),
Day = Hours.slice(0, -1),
Week = [YEAR, WEEK],
Month = [YEAR, MONTH],
Year = [YEAR];
const intervals = [[Seconds, 1, durationSecond], [Seconds, 5, 5 * durationSecond], [Seconds, 15, 15 * durationSecond], [Seconds, 30, 30 * durationSecond], [Minutes, 1, durationMinute], [Minutes, 5, 5 * durationMinute], [Minutes, 15, 15 * durationMinute], [Minutes, 30, 30 * durationMinute], [Hours, 1, durationHour], [Hours, 3, 3 * durationHour], [Hours, 6, 6 * durationHour], [Hours, 12, 12 * durationHour], [Day, 1, durationDay], [Week, 1, durationWeek], [Month, 1, durationMonth], [Month, 3, 3 * durationMonth], [Year, 1, durationYear]];
function bin (opt) {
const ext = opt.extent,
max = opt.maxbins || 40,
target = Math.abs(span(ext)) / max;
let i = bisector(i => i[2]).right(intervals, target),
units,
step;
if (i === intervals.length) {
units = Year, step = tickStep(ext[0] / durationYear, ext[1] / durationYear, max);
} else if (i) {
i = intervals[target / intervals[i - 1][2] < intervals[i][2] / target ? i - 1 : i];
units = i[0];
step = i[1];
} else {
units = Milli;
step = Math.max(tickStep(ext[0], ext[1], max), 1);
}
return {
units,
step
};
}
export { DATE, DAY, DAYOFYEAR, HOURS, MILLISECONDS, MINUTES, MONTH, QUARTER, SECONDS, TIME_UNITS, WEEK, YEAR, dayofyear, bin as timeBin, timeFloor, timeInterval, timeOffset, timeSequence, timeUnitSpecifier, timeUnits, utcFloor, utcInterval, utcOffset, utcSequence, utcdayofyear, utcweek, week };
//# sourceMappingURL=vega-time.js.map