Skip to content

Commit

Permalink
Use SERIAL_NUMBER for Date parsing
Browse files Browse the repository at this point in the history
Signed-off-by: Cédric Boirard <[email protected]>
  • Loading branch information
triozer committed Feb 11, 2025
1 parent 08c2452 commit 4fc5337
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 5 deletions.
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)
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

0 comments on commit 4fc5337

Please sign in to comment.