feat(core): adapt date fields in database for notion import (#14111)

This is related to issue/feature request
https://github.com/toeverything/AFFiNE/issues/13962.

This PR extends the Notion import functionality to properly handle date
fields from databases. Previously, these were imported as text (see
photo below), which served little purpose. These Notion date fields are
now parsed as actual dates, and imported to AFFiNE as epoch time (which
is what the date field in AFFiNe expects). Because of this, even date
fields with time (e.g. 09:00 AM) are also handled correctly - although
they are only shown as dates, since AFFiNE's `Date` field does not
support time.

Tested with several Notion imports both with and without time, and they
all seem to work correctly.


Affected files: 
- blocksuite/affine/blocks/database/src/adapters/notion-html.ts

Old: 
<img width="802" height="305" alt="image"
src="https://github.com/user-attachments/assets/44019dba-cffb-4a30-a5ea-69cd9f86e0a1"
/>

New: 
<img width="804" height="271" alt="image"
src="https://github.com/user-attachments/assets/3f52f328-7ee3-4754-9726-10dcfa0f8462"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Enhanced Notion imports with automatic date column detection. When
importing Notion databases, date fields are now automatically
recognized, properly configured as date columns, and formatted
correctly. This improvement ensures accurate data preservation,
eliminates manual type corrections, and provides a streamlined import
experience for all users working with date-rich Notion databases.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Daniel Dybing
2025-12-16 03:55:34 +01:00
committed by GitHub
parent f5076a37ae
commit 66407f2b2f

View File

@@ -15,6 +15,7 @@ const ColumnClassMap: Record<string, string> = {
typesCheckbox: 'checkbox',
typesText: 'rich-text',
typesTitle: 'title',
typesDate: 'date',
};
const NotionDatabaseToken = '.collection-content';
@@ -165,7 +166,36 @@ export const databaseBlockNotionHtmlAdapterMatcher: BlockNotionHtmlAdapterMatche
if (!column) {
return;
}
if (HastUtils.querySelector(child, '.selected-value')) {
// Check for <time> element to find date field from Notion.
if (HastUtils.querySelector(child, 'time')) {
const timeElement = HastUtils.querySelector(child, 'time');
let rawColumnData =
HastUtils.getTextContent(timeElement).trim();
if (rawColumnData.startsWith('@')) {
rawColumnData = rawColumnData.slice(1);
}
const columnDate = new Date(rawColumnData);
const timestamp = columnDate.getTime();
if (!Number.isNaN(timestamp)) {
column.data = {};
if (column.type !== 'date') {
column.type = 'date';
}
row[column.id] = {
columnId: column.id,
value: timestamp,
};
} else {
row[column.id] = {
columnId: column.id,
value: HastUtils.getTextContent(child),
};
}
} else if (HastUtils.querySelector(child, '.selected-value')) {
if (!('options' in column.data)) {
column.data.options = [];
}