Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use SERIAL_NUMBER for Date parsing #165

Merged
merged 1 commit into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions plugins/google-sheets/src/pages/MapSheetFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ const inferFieldType = (cellValue: CellValue): CollectionFieldType => {

// If the cell value contains a newline, it's probably a formatted text field
if (cellValueLowered.includes("\n")) return "formattedText"
const maybeDate = new Date(cellValueLowered)
if (!Number.isNaN(maybeDate.getTime())) return "date"
if (/^#[0-9a-f]{6}$/.test(cellValueLowered)) return "color"
if (/<[a-z][\s\S]*>/.test(cellValueLowered)) return "formattedText"

Expand Down
30 changes: 27 additions & 3 deletions plugins/google-sheets/src/sheets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ function fetchSheet(spreadsheetId: string, sheetTitle: string, range?: string) {
query: {
range: range ?? sheetTitle,
valueRenderOption: "UNFORMATTED_VALUE",
dateTimeRenderOption: "FORMATTED_STRING",
dateTimeRenderOption: "SERIAL_NUMBER",
},
})
}
Expand Down Expand Up @@ -279,6 +279,30 @@ export interface SyncMutationOptions {
lastSyncedTime: string | null
}

const BASE_DATE_1900 = new Date(Date.UTC(1899, 11, 30))
const BASE_DATE_1904 = new Date(Date.UTC(1904, 0, 1))
const MS_PER_DAY = 24 * 60 * 60 * 1000 // hours * minutes * seconds * milliseconds

/**
* Extracts a date from a serial number in Lotus 1-2-3 date representation.
*/
function extractDateFromSerialNumber(serialNumber: number) {
// Use 1900 system by default, but if date is before 1904,
// switch to 1904 system
let baseDate = BASE_DATE_1900
const date1900 = new Date(BASE_DATE_1900.getTime() + serialNumber * MS_PER_DAY)

if (date1900 < BASE_DATE_1904) {
baseDate = BASE_DATE_1904
}

const wholeDays = Math.floor(serialNumber)
triozer marked this conversation as resolved.
Show resolved Hide resolved
const fractionalDay = serialNumber - wholeDays
const milliseconds = Math.round(fractionalDay * MS_PER_DAY)

return new Date(baseDate.getTime() + wholeDays * MS_PER_DAY + milliseconds)
}

function getFieldValue(fieldType: CollectionFieldType, cellValue: CellValue) {
switch (fieldType) {
case "number": {
Expand All @@ -293,9 +317,9 @@ function getFieldValue(fieldType: CollectionFieldType, cellValue: CellValue) {
return CELL_BOOLEAN_VALUES.includes(cellValue)
}
case "date": {
if (typeof cellValue !== "string") return null
if (typeof cellValue !== "number") return null
try {
const date = new Date(Date.parse(cellValue))
const date = extractDateFromSerialNumber(cellValue)
return date.toISOString()
} catch {
return null
Expand Down