Skip to content

better date and timestamp support - #282

Merged
Fil merged 3 commits into
mainfrom
fil/more-dates
Nov 3, 2026
Merged

Fil merged 3 commits into
mainfrom
fil/more-dates

Conversation

@Fil

@Fil Fil commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

for flechette, support more date formats.

for apache-arrow: closes #269

Related uwdata/flechette#74

@Fil
Fil requested a review from mbostock October 7, 2026 14:53
Comment thread src/table.js Outdated
// time unit; convert them to milliseconds, as Flechette does by default.
function formatTimestamp(data, column) {
const {unit} = data.schema.fields.find((d) => d.name === column).type;
return (value) => formatDate(typeof value === "bigint" ? toMilliseconds(value, unit) : value);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to know from the data.schema.fields whether it will be a bigint so that you don’t have to check every value?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so; running through (potentially) the whole dataset to check the first non-null value might be more costly (in the worst case, say if you have millions of null values before the first BigInt), than returning this function that checks every value.

Comment thread src/table.js Outdated
Comment on lines +353 to +354
const {unit} = data.schema.fields.find((d) => d.name === column).type;
return (value) => formatDate(typeof value === "bigint" ? toMilliseconds(value, unit) : value);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concretely: the diff below lifts the test — however the drawback is that it might have to read the whole dataset. Since we wouldn't in the general case want to format millions of rows, I think the simpler version is probably better?

-  const {unit} = data.schema.fields.find((d) => d.name === column).type;
-  return (value) => formatDate(typeof value === "bigint" ? toMilliseconds(value, unit) : value);
+  for (const value of data.getChild(column)) {
+    if (value == null) continue;
+    if (typeof value !== "bigint") break;
+    const {unit} = data.schema.fields.find((d) => d.name === column).type;
+    return (value) => formatDate(toMilliseconds(value, unit));
+  }
+  return formatDate;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s not what I meant. I meant looking at the schema to avoid checking every value. Checking every value ahead of time is worse than what you were previously doing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that's why I didn't push this. There's nothing in the schema that tells us whether we'll get BigInts, and the options are not saved in the file.

Comment thread src/table.js Outdated
switch (type(data, column)) {
case "number": format[column] = formatLocaleNumber(locale); break;
case "date": format[column] = formatDate; break;
case "timestamp": format[column] = formatTimestamp(data, column); break;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn’t pass data into the formatTimestamp function here; you should instead fold the unit test into the earlier type check to have separate seconds, milliseconds, microseconds, and nanoseconds types…

Suggested change
case "timestamp": format[column] = formatTimestamp(data, column); break;
case "seconds": format[column] = formatSeconds; break;
case "milliseconds": format[column] = formatMilliseconds; break;
case "microseconds": format[column] = formatMicroseconds; break;
case "nanoseconds": format[column] = formatNanoseconds; break;

More generally “timestamp” isn’t sufficiently capturing what the type of this column is, which means the formatter is doing too much work, and is reliant on the data being an Arrow Table. That logic is supposed to live in the type checking routine, not in the formatter.

Comment thread src/table.js Outdated
return "date";
case 10: // Timestamp
return field.type.unit === 1 ? "date" : "number"; // millisecond
return "timestamp";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return "timestamp";
const unit = field.type.unit;
return unit === 0 ? "seconds" : unit === 1 ? "milliseconds" : unit === 2 ? "microseconds" : unit === 3 ? "nanoseconds" : undefined;

@Fil
Fil merged commit 5cd088f into main Nov 3, 2026
1 check passed
@Fil
Fil deleted the fil/more-dates branch November 3, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dates not detected in arrow tables

2 participants