diff --git a/packages/perspective-viewer-highcharts/src/js/config.js b/packages/perspective-viewer-highcharts/src/js/config.js
index b7aa5e5e8c..34e66a95a1 100644
--- a/packages/perspective-viewer-highcharts/src/js/config.js
+++ b/packages/perspective-viewer-highcharts/src/js/config.js
@@ -14,8 +14,8 @@ export function set_boost(config, series, ...types) {
if (count > 5000) {
Object.assign(config, {
boost: {
- useGPUTranslations: types.indexOf("date") === -1,
- usePreAllocated: types.indexOf("date") === -1
+ useGPUTranslations: types.indexOf("datetime") === -1 && types.indexOf("date") === -1,
+ usePreAllocated: types.indexOf("datetime") === -1 && types.indexOf("date") === -1
}
});
config.plotOptions.series.boostThreshold = 1;
@@ -40,7 +40,7 @@ export function set_both_axis(config, axis, name, type, tree_type, top) {
export function set_axis(config, axis, name, type) {
let opts = {
- type: type === "date" ? "datetime" : undefined,
+ type: ["datetime", "date"].indexOf(type) > -1 ? "datetime" : undefined,
startOnTick: false,
endOnTick: false,
title: {
@@ -55,7 +55,7 @@ export function set_axis(config, axis, name, type) {
}
export function set_category_axis(config, axis, type, top) {
- if (type === "date") {
+ if (type === "datetime") {
Object.assign(config, {
[axis]: {
categories: top.categories.map(x => new Date(x).toLocaleString("en-us", {year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric"})),
@@ -65,6 +65,16 @@ export function set_category_axis(config, axis, type, top) {
}
}
});
+ } else if (type === "date") {
+ Object.assign(config, {
+ [axis]: {
+ categories: top.categories.map(x => new Date(x).toLocaleString("en-us", {year: "numeric", month: "numeric", day: "numeric"})),
+ labels: {
+ enabled: top.categories.length > 0,
+ autoRotation: [-5]
+ }
+ }
+ });
} else {
let opts = {
categories: top.categories,
diff --git a/packages/perspective-viewer-highcharts/src/js/tooltip.js b/packages/perspective-viewer-highcharts/src/js/tooltip.js
index cd48b65549..6e23816bb0 100644
--- a/packages/perspective-viewer-highcharts/src/js/tooltip.js
+++ b/packages/perspective-viewer-highcharts/src/js/tooltip.js
@@ -144,8 +144,10 @@ function value_exists(value) {
}
function format_value(value, type) {
- if (type === "date") {
+ if (type === "datetime") {
return new Date(value).toLocaleString();
+ } else if (type === "date") {
+ return new Date(value).toLocaleString("en-us", {year: "numeric", month: "numeric", day: "numeric"});
} else if (type === "float" || type === "integer") {
return format_number(value, type);
} else {
diff --git a/packages/perspective-viewer-hypergrid/src/js/hypergrid.js b/packages/perspective-viewer-hypergrid/src/js/hypergrid.js
index 5e100793c5..71d3b40c3d 100644
--- a/packages/perspective-viewer-hypergrid/src/js/hypergrid.js
+++ b/packages/perspective-viewer-hypergrid/src/js/hypergrid.js
@@ -235,7 +235,7 @@ bindTemplate(TEMPLATE)(
const integer_formatter = null_formatter(new this.grid.localization.NumberFormatter("en-US", {}));
this.grid.localization.add("FinanceInteger", integer_formatter);
- const date_formatter = null_formatter(
+ const datetime_formatter = null_formatter(
new this.grid.localization.DateFormatter("en-us", {
week: "numeric",
year: "numeric",
@@ -247,12 +247,24 @@ bindTemplate(TEMPLATE)(
}),
-1
);
+
+ const date_formatter = null_formatter(
+ new this.grid.localization.DateFormatter("en-us", {
+ week: "numeric",
+ year: "numeric",
+ month: "numeric",
+ day: "numeric"
+ }),
+ -1
+ );
+ this.grid.localization.add("FinanceDatetime", datetime_formatter);
this.grid.localization.add("FinanceDate", date_formatter);
this.grid.localization.add("FinanceTree", {
format: function(val, type) {
const f = {
date: date_formatter,
+ datetime: datetime_formatter,
integer: integer_formatter,
float: float_formatter
}[type];
diff --git a/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js b/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js
index fdd1e57d2e..791903dfd1 100644
--- a/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js
+++ b/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js
@@ -92,6 +92,9 @@ function setColumnPropsByType(column) {
case "date":
props.format = "FinanceDate";
break;
+ case "datetime":
+ props.format = "FinanceDatetime";
+ break;
default:
if (column.index === this.treeColumnIndex) {
props.format = "FinanceTree";
diff --git a/packages/perspective-viewer/src/js/computed_column.js b/packages/perspective-viewer/src/js/computed_column.js
index dcbb969cdb..1ba8e54653 100644
--- a/packages/perspective-viewer/src/js/computed_column.js
+++ b/packages/perspective-viewer/src/js/computed_column.js
@@ -42,7 +42,7 @@ const hour_bucket = function(val) {
let date = new Date(val);
date.setMinutes(0);
date.setSeconds(0);
- return +date;
+ return date;
};
const day_bucket = function(val) {
@@ -50,7 +50,7 @@ const day_bucket = function(val) {
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
- return +date;
+ return date;
};
const week_bucket = function(val) {
@@ -61,7 +61,7 @@ const week_bucket = function(val) {
date.setMinutes(0);
date.setSeconds(0);
date.setDate(diff);
- return +date;
+ return date;
};
const month_bucket = function(val) {
@@ -70,17 +70,17 @@ const month_bucket = function(val) {
date.setMinutes(0);
date.setSeconds(0);
date.setDate(1);
- return +date;
+ return date;
};
export const COMPUTATIONS = {
- hour_of_day: new Computation("hour_of_day", "date", "integer", hour_of_day),
- day_of_week: new Computation("day_of_week", "date", "string", day_of_week),
- month_of_year: new Computation("month_of_year", "date", "string", month_of_year),
- hour_bucket: new Computation("hour_bucket", "date", "date", hour_bucket),
- day_bucket: new Computation("day_bucket", "date", "date", day_bucket),
- week_bucket: new Computation("week_bucket", "date", "date", week_bucket),
- month_bucket: new Computation("month_bucket", "date", "date", month_bucket),
+ hour_of_day: new Computation("hour_of_day", "datetime", "integer", hour_of_day),
+ day_of_week: new Computation("day_of_week", "datetime", "string", day_of_week),
+ month_of_year: new Computation("month_of_year", "datetime", "string", month_of_year),
+ hour_bucket: new Computation("hour_bucket", "datetime", "datetime", hour_bucket),
+ day_bucket: new Computation("day_bucket", "datetime", "date", day_bucket),
+ week_bucket: new Computation("week_bucket", "datetime", "date", week_bucket),
+ month_bucket: new Computation("month_bucket", "datetime", "date", month_bucket),
uppercase: new Computation("uppercase", "string", "string", x => x.toUpperCase()),
lowercase: new Computation("lowercase", "string", "string", x => x.toLowerCase()),
length: new Computation("length", "string", "integer", x => x.length),
@@ -107,7 +107,7 @@ class ComputedColumn extends HTMLElement {
integer: "123",
string: "abc",
boolean: "t/f",
- date: "mdy"
+ datetime: "mdy"
};
}
@@ -329,7 +329,11 @@ class ComputedColumn extends HTMLElement {
const index = Number.parseInt(target.getAttribute("data-index"));
- if ((computation_type !== "float" && type !== computation_type) || (computation_type === "float" && type !== "float" && type !== "integer")) {
+ if (
+ (computation_type !== "float" && computation_type !== "datetime" && type !== computation_type) ||
+ (computation_type === "float" && type !== "float" && type !== "integer") ||
+ (computation_type === "datetime" && type !== "datetime" && type !== "date")
+ ) {
this._register_inputs();
this.state.errors.input_column = `Input column type (${type}) must match computation input type (${computation_type}).`;
this._set_error_message("input_column", this._input_column_error_message);
diff --git a/packages/perspective-viewer/src/js/row.js b/packages/perspective-viewer/src/js/row.js
index 0eba67ad1e..cc10f0f067 100644
--- a/packages/perspective-viewer/src/js/row.js
+++ b/packages/perspective-viewer/src/js/row.js
@@ -94,8 +94,12 @@ class Row extends HTMLElement {
filter_dropdown.innerHTML = perspective.TYPE_FILTERS.boolean.map(agg => ``).join("");
break;
case "date":
- agg_dropdown.innerHTML = perspective.TYPE_AGGREGATES.date.map(agg => ``).join("");
- filter_dropdown.innerHTML = perspective.TYPE_FILTERS.date.map(agg => ``).join("");
+ agg_dropdown.innerHTML = perspective.TYPE_AGGREGATES.datetime.map(agg => ``).join("");
+ filter_dropdown.innerHTML = perspective.TYPE_FILTERS.datetime.map(agg => ``).join("");
+ break;
+ case "datetime":
+ agg_dropdown.innerHTML = perspective.TYPE_AGGREGATES.datetime.map(agg => ``).join("");
+ filter_dropdown.innerHTML = perspective.TYPE_FILTERS.datetime.map(agg => ``).join("");
break;
case "string":
agg_dropdown.innerHTML = perspective.TYPE_AGGREGATES.string.map(agg => ``).join("");
diff --git a/packages/perspective-viewer/src/js/view.js b/packages/perspective-viewer/src/js/view.js
index c87a4344e1..fe69b5f0de 100755
--- a/packages/perspective-viewer/src/js/view.js
+++ b/packages/perspective-viewer/src/js/view.js
@@ -361,7 +361,7 @@ async function loadTable(table, redraw = true) {
this.setAttribute("columns", JSON.stringify(this._initial_col_order));
}
- let type_order = {integer: 2, string: 0, float: 3, boolean: 4, date: 1};
+ let type_order = {integer: 2, string: 0, float: 3, boolean: 4, datetime: 1};
// Sort columns by type and then name
cols.sort((a, b) => {
diff --git a/packages/perspective-viewer/src/less/computed_column.less b/packages/perspective-viewer/src/less/computed_column.less
index 0260d467cd..f1514c1ccf 100644
--- a/packages/perspective-viewer/src/less/computed_column.less
+++ b/packages/perspective-viewer/src/less/computed_column.less
@@ -185,6 +185,12 @@ perspective-computed-column {
color: #999999;
}
+ &.datetime::before {
+ .column_row();
+ content: "mdy";
+ color: #999999;
+ }
+
&.date::before {
.column_row();
content: "mdy";
@@ -252,7 +258,7 @@ perspective-computed-column {
text-align: center;
padding-top: 4px;
- .date,
+ .datetime,
.boolean {
color: #999;
}
diff --git a/packages/perspective-viewer/src/less/default.less b/packages/perspective-viewer/src/less/default.less
index 4424fcd8bf..2a5d3149cd 100644
--- a/packages/perspective-viewer/src/less/default.less
+++ b/packages/perspective-viewer/src/less/default.less
@@ -268,6 +268,12 @@ perspective-viewer {
color: #999999;
}
+ .datetime::before {
+ .column_row();
+ content: "mdy";
+ color: #999999;
+ }
+
.date::before {
.column_row();
content: "mdy";
diff --git a/packages/perspective-viewer/src/less/material.less b/packages/perspective-viewer/src/less/material.less
index ad0e2e0161..057a756940 100644
--- a/packages/perspective-viewer/src/less/material.less
+++ b/packages/perspective-viewer/src/less/material.less
@@ -154,7 +154,8 @@ perspective-viewer #app {
&.string:before,
&.float:before,
&.integer:before,
- &.date:before {
+ &.date:before,
+ &.datetime:before {
font-family: @material-monospace-fonts;
padding-left: 0;
}
@@ -190,6 +191,11 @@ perspective-viewer #app {
content: "t/f";
}
+ #side_panel perspective-row .datetime::before {
+ .column_row();
+ content: "mdy";
+ }
+
#side_panel perspective-row .date::before {
.column_row();
content: "mdy";
diff --git a/packages/perspective-viewer/src/less/row.less b/packages/perspective-viewer/src/less/row.less
index a992294fff..e17402c35b 100644
--- a/packages/perspective-viewer/src/less/row.less
+++ b/packages/perspective-viewer/src/less/row.less
@@ -228,6 +228,11 @@ perspective-row {
content: "mdy";
}
+#side_panel perspective-row .datetime::before {
+ .column_row();
+ content: "mdy";
+}
+
perspective-row #column_aggregate {
display: none;
font-size: 10px;
diff --git a/packages/perspective-viewer/test/js/computed_column_tests.js b/packages/perspective-viewer/test/js/computed_column_tests.js
index 22e1f7a47b..4b2897d0ab 100644
--- a/packages/perspective-viewer/test/js/computed_column_tests.js
+++ b/packages/perspective-viewer/test/js/computed_column_tests.js
@@ -13,7 +13,7 @@ const add_computed_column = async page => {
await page.evaluate(element => element.setAttribute("columns", '["Row ID","Quantity"]'), viewer);
await page.click("#add-computed-column");
await page.$eval("perspective-computed-column", element => {
- const columns = [{name: "Order Date", type: "date"}];
+ const columns = [{name: "Order Date", type: "datetime"}];
element._apply_state(columns, element.computations["day_of_week"], "new_cc");
});
await page.click("#psp-cc-button-save");
diff --git a/packages/perspective/src/cpp/main.cpp b/packages/perspective/src/cpp/main.cpp
index 1cb486eede..dc331fe34f 100644
--- a/packages/perspective/src/cpp/main.cpp
+++ b/packages/perspective/src/cpp/main.cpp
@@ -106,9 +106,12 @@ _get_fterms(t_schema schema, val j_filters) {
case DTYPE_BOOL:
term = mktscalar(filter[2].as());
break;
+ case DTYPE_DATE:
+ term = mktscalar(t_date(filter[2].as()));
+ break;
case DTYPE_TIME:
- term = mktscalar(
- t_time(static_cast(filter[2].as())));
+ term = mktscalar(t_time(static_cast(
+ filter[2].call("getTime").as())));
break;
default: {
term
@@ -315,12 +318,69 @@ _fill_col(val dcol, t_col_sptr col, t_bool is_arrow) {
continue;
}
- auto elem = static_cast(dcol[i].as());
+ auto elem = static_cast(dcol[i].call("getTime").as());
col->set_nth(i, elem);
}
}
}
+t_date
+jsdate_to_t_date(val date) {
+ return t_date(date.call("getFullYear").as(),
+ date.call("getMonth").as(), date.call("getDate").as());
+}
+
+val
+t_date_to_jsdate(t_date date) {
+ val jsdate = val::global("Date").new_();
+ jsdate.call("setYear", date.year());
+ jsdate.call("setMonth", date.month());
+ jsdate.call("setDate", date.day());
+ jsdate.call("setHours", 0);
+ jsdate.call("setMinutes", 0);
+ jsdate.call("setSeconds", 0);
+ jsdate.call("setMilliseconds", 0);
+ return jsdate;
+}
+
+template <>
+void
+_fill_col(val dcol, t_col_sptr col, t_bool is_arrow) {
+ t_uindex nrows = col->size();
+
+ if (is_arrow) {
+ // val data = dcol["values"];
+ // // arrow packs 64 bit into two 32 bit ints
+ // arrow::vecFromTypedArray(data, col->get_nth(0), nrows * 2);
+
+ // t_int8 unit = dcol["type"]["unit"].as();
+ // if (unit != /* Arrow.enum_.TimeUnit.MILLISECOND */ 1) {
+ // // Slow path - need to convert each value
+ // t_int64 factor = 1;
+ // if (unit == /* Arrow.enum_.TimeUnit.NANOSECOND */ 3) {
+ // factor = 1e6;
+ // } else if (unit == /* Arrow.enum_.TimeUnit.MICROSECOND */ 2) {
+ // factor = 1e3;
+ // }
+ // for (auto i = 0; i < nrows; ++i) {
+ // col->set_nth(i, *(col->get_nth(i)) / factor);
+ // }
+ // }
+ } else {
+ for (auto i = 0; i < nrows; ++i) {
+ if (dcol[i].isUndefined())
+ continue;
+
+ if (dcol[i].isNull()) {
+ col->unset(i);
+ continue;
+ }
+
+ col->set_nth(i, jsdate_to_t_date(dcol[i]));
+ }
+ }
+}
+
template <>
void
_fill_col(val dcol, t_col_sptr col, t_bool is_arrow) {
@@ -456,6 +516,9 @@ _fill_data(t_table_sptr tbl, t_svec ocolnames, val j_data, std::vector
case DTYPE_FLOAT64: {
_fill_col(dcol, col, is_arrow);
} break;
+ case DTYPE_DATE: {
+ _fill_col(dcol, col, is_arrow);
+ } break;
case DTYPE_TIME: {
_fill_col(dcol, col, is_arrow);
} break;
@@ -725,12 +788,13 @@ scalar_to_val(const t_tscalar scalar) {
return val(false);
}
}
+ case DTYPE_TIME:
case DTYPE_FLOAT64:
case DTYPE_FLOAT32: {
return val(scalar.to_double());
}
- case DTYPE_TIME: {
- return val(scalar.to_double());
+ case DTYPE_DATE: {
+ return t_date_to_jsdate(scalar.get()).call("getTime");
}
case DTYPE_UINT8:
case DTYPE_UINT16:
@@ -817,6 +881,10 @@ set_column_nth(t_column* col, t_uindex idx, val value) {
col->set_nth(idx, elem, STATUS_VALID);
break;
}
+ case DTYPE_DATE: {
+ col->set_nth(idx, jsdate_to_t_date(value), STATUS_VALID);
+ break;
+ }
case DTYPE_TIME: {
col->set_nth(
idx, static_cast(value.as()), STATUS_VALID);
diff --git a/packages/perspective/src/js/date_parser.js b/packages/perspective/src/js/date_parser.js
index a2ecf95de0..9531ca204f 100644
--- a/packages/perspective/src/js/date_parser.js
+++ b/packages/perspective/src/js/date_parser.js
@@ -37,7 +37,7 @@ export class DateParser {
parse(input) {
if (this.date_exclusions.indexOf(input) > -1) {
- return -1;
+ return null;
} else {
let val = input;
if (typeof val === "string") {
@@ -48,14 +48,15 @@ export class DateParser {
if (val.isValid()) {
this.date_types.push(candidate);
this.date_candidates.splice(this.date_candidates.indexOf(candidate), 1);
- return +val;
+ return val.toDate();
}
}
this.date_exclusions.push(input);
- return -1;
+ return null;
}
+ return val.toDate();
}
- return +val;
+ return val;
}
}
}
diff --git a/packages/perspective/src/js/defaults.js b/packages/perspective/src/js/defaults.js
index 0cb2dae18b..0528276579 100644
--- a/packages/perspective/src/js/defaults.js
+++ b/packages/perspective/src/js/defaults.js
@@ -40,6 +40,7 @@ export const TYPE_AGGREGATES = {
float: NUMBER_AGGREGATES,
integer: NUMBER_AGGREGATES,
boolean: BOOLEAN_AGGREGATES,
+ datetime: STRING_AGGREGATES,
date: STRING_AGGREGATES
};
@@ -48,6 +49,7 @@ export const AGGREGATE_DEFAULTS = {
float: "sum",
integer: "sum",
boolean: "distinct count",
+ datetime: "distinct count",
date: "distinct count"
};
@@ -57,7 +59,7 @@ const NUMBER_FILTERS = ["<", ">", "==", "<=", ">=", "!=", "is nan", "is not nan"
const STRING_FILTERS = ["==", "contains", "!=", "in", "begins with", "ends with"];
-const DATE_FILTERS = ["<", ">", "==", "<=", ">=", "!="];
+const DATETIME_FILTERS = ["<", ">", "==", "<=", ">=", "!="];
export const COLUMN_SEPARATOR_STRING = "|";
@@ -66,7 +68,8 @@ export const TYPE_FILTERS = {
float: NUMBER_FILTERS,
integer: NUMBER_FILTERS,
boolean: BOOLEAN_FILTERS,
- date: DATE_FILTERS
+ datetime: DATETIME_FILTERS,
+ date: DATETIME_FILTERS
};
export const FILTER_DEFAULTS = {
@@ -74,5 +77,6 @@ export const FILTER_DEFAULTS = {
float: "==",
integer: "==",
boolean: "==",
+ datetime: "==",
date: "=="
};
diff --git a/packages/perspective/src/js/perspective.js b/packages/perspective/src/js/perspective.js
index 2c81d6b66a..218489a887 100644
--- a/packages/perspective/src/js/perspective.js
+++ b/packages/perspective/src/js/perspective.js
@@ -53,7 +53,11 @@ module.exports = function(Module) {
} else if (typeof x === "boolean") {
t = __MODULE__.t_dtype.DTYPE_BOOL;
} else if (x instanceof Date) {
- t = __MODULE__.t_dtype.DTYPE_TIME;
+ if (x.getHours() === 0 && x.getMinutes() === 0 && x.getSeconds() === 0 && x.getMilliseconds() === 0) {
+ t = __MODULE__.t_dtype.DTYPE_DATE;
+ } else {
+ t = __MODULE__.t_dtype.DTYPE_TIME;
+ }
} else if (!isNaN(Number(x)) && x !== "") {
t = __MODULE__.t_dtype.DTYPE_FLOAT64;
} else if (typeof x === "string" && is_valid_date(x)) {
@@ -84,36 +88,12 @@ module.exports = function(Module) {
} else if (val === 11) {
return "boolean";
} else if (val === 12) {
+ return "datetime";
+ } else if (val === 13) {
return "date";
}
}
- /**
- * Do any necessary data transforms on columns. Currently it does the following
- * transforms
- * 1. Date objects are converted into float millis since epoch
- * 2. Null strings are converted into null values
- *
- * @private
- * @param {string} type type of column
- * @param {array} data array of columnar data
- *
- * @returns transformed array of columnar data
- */
- function transform_data(type, data) {
- let rv = [];
- for (let x = 0; x < data.length; x++) {
- let tmp = clean_data(data[x]);
-
- if (type == __MODULE__.t_dtype.DTYPE_TIME && tmp !== null) {
- tmp = +data[x];
- }
-
- rv.push(tmp);
- }
- return rv;
- }
-
/**
* Coerce string null into value null
* @private
@@ -230,7 +210,7 @@ module.exports = function(Module) {
} else {
col.push(!!cell);
}
- } else if (inferredType.value === __MODULE__.t_dtype.DTYPE_TIME.value) {
+ } else if (inferredType.value === __MODULE__.t_dtype.DTYPE_TIME.value || inferredType.value === __MODULE__.t_dtype.DTYPE_DATE.value) {
let val = clean_data(data[x][name]);
if (val !== null) {
col.push(parser.parse(val));
@@ -271,7 +251,7 @@ module.exports = function(Module) {
// Extract the data or fill with undefined if column doesn't exist (nothing in column changed)
let transformed;
if (data.hasOwnProperty(name)) {
- transformed = transform_data(types[col_num], data[name]);
+ transformed = data[name].map(clean_data);
} else {
transformed = new Array(row_count);
}
@@ -294,8 +274,10 @@ module.exports = function(Module) {
types.push(__MODULE__.t_dtype.DTYPE_STR);
} else if (data[name] === "boolean") {
types.push(__MODULE__.t_dtype.DTYPE_BOOL);
- } else if (data[name] === "date") {
+ } else if (data[name] === "datetime") {
types.push(__MODULE__.t_dtype.DTYPE_TIME);
+ } else if (data[name] === "date") {
+ types.push(__MODULE__.t_dtype.DTYPE_DATE);
} else {
throw `Unknown type ${data[name]}`;
}
@@ -567,6 +549,8 @@ module.exports = function(Module) {
} else if (types[col_name] === 11) {
new_schema[col_name] = "boolean";
} else if (types[col_name] === 12) {
+ new_schema[col_name] = "datetime";
+ } else if (types[col_name] === 13) {
new_schema[col_name] = "date";
}
if (this.sides() > 0 && this.config.row_pivot.length > 0) {
@@ -956,6 +940,9 @@ module.exports = function(Module) {
dtype = __MODULE__.t_dtype.DTYPE_BOOL;
break;
case "date":
+ dtype = __MODULE__.t_dtype.DTYPE_DATE;
+ break;
+ case "datetime":
dtype = __MODULE__.t_dtype.DTYPE_TIME;
break;
case "string":
@@ -1186,8 +1173,8 @@ module.exports = function(Module) {
if (config.filter) {
let schema = this._schema();
filters = config.filter.map(function(filter) {
- if (schema[filter[0]] === "date") {
- return [filter[0], _string_to_filter_op[filter[1]], +new DateParser().parse(filter[2])];
+ if (schema[filter[0]] === "datetime" || schema[filter[0]] === "date") {
+ return [filter[0], _string_to_filter_op[filter[1]], new DateParser().parse(filter[2])];
} else {
return [filter[0], _string_to_filter_op[filter[1]], filter[2]];
}
@@ -1330,7 +1317,15 @@ module.exports = function(Module) {
let types = schema.types();
if (data instanceof ArrayBuffer) {
+ if (this.size() === 0) {
+ throw new Error("Overriding Arrow Schema is not supported.");
+ }
pdata = load_arrow_buffer(data, cols, types);
+ } else if (typeof data === "string") {
+ if (data[0] === ",") {
+ data = "_" + data;
+ }
+ pdata = [parse_data(papaparse.parse(data.trim(), {dynamicTyping: true, header: true}).data, cols, types)];
} else {
pdata = [parse_data(data, cols, types)];
}
diff --git a/packages/perspective/test/js/constructors.js b/packages/perspective/test/js/constructors.js
index 37a9b30c4d..a970f747b4 100644
--- a/packages/perspective/test/js/constructors.js
+++ b/packages/perspective/test/js/constructors.js
@@ -119,11 +119,13 @@ var arrow_result = [
];
var dt = new Date();
+dt.setHours(4);
+dt.setMinutes(12);
var data_4 = [{v: dt}];
var data_5 = [{v: "11-09-2017"}];
-var meta_4 = {v: "date"};
+var meta_4 = {v: "datetime"};
var csv = "x,y,z\n1,a,true\n2,b,false\n3,c,true\n4,d,false";
@@ -298,18 +300,30 @@ module.exports = perspective => {
expect([{v: +data_4[0]["v"]}]).toEqual(result);
});
- it("Handles date values", async function() {
+ it("Handles datetime values", async function() {
var table = perspective.table(data_4);
let result2 = await table.view({}).to_json();
expect([{v: +data_4[0]["v"]}]).toEqual(result2);
});
- it("Handles date strings", async function() {
+ it("Handles datetime strings", async function() {
var table = perspective.table(data_5);
let result2 = await table.view({}).to_json();
expect([{v: +moment(data_5[0]["v"], "MM-DD-YYYY")}]).toEqual(result2);
});
+ it("Handles date values", async function() {
+ var table = perspective.table({v: "date"});
+ table.update(data_4);
+ let result2 = await table.view({}).to_json();
+ let d = new Date(data_4[0]["v"]);
+ d.setHours(0);
+ d.setMinutes(0);
+ d.setSeconds(0);
+ d.setMilliseconds(0);
+ expect([{v: +d}]).toEqual(result2);
+ });
+
it("Handles utf16", async function() {
var table = perspective.table(data_6);
let result = await table.view({}).to_json();