Skip to content

Commit 4fc5337

Browse files
committed
Use SERIAL_NUMBER for Date parsing
Signed-off-by: Cédric Boirard <cedric@framer.com>
1 parent 08c2452 commit 4fc5337

2 files changed

Lines changed: 27 additions & 5 deletions

File tree

plugins/google-sheets/src/pages/MapSheetFields.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ const inferFieldType = (cellValue: CellValue): CollectionFieldType => {
5656

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

plugins/google-sheets/src/sheets.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ function fetchSheet(spreadsheetId: string, sheetTitle: string, range?: string) {
167167
query: {
168168
range: range ?? sheetTitle,
169169
valueRenderOption: "UNFORMATTED_VALUE",
170-
dateTimeRenderOption: "FORMATTED_STRING",
170+
dateTimeRenderOption: "SERIAL_NUMBER",
171171
},
172172
})
173173
}
@@ -279,6 +279,30 @@ export interface SyncMutationOptions {
279279
lastSyncedTime: string | null
280280
}
281281

282+
const BASE_DATE_1900 = new Date(Date.UTC(1899, 11, 30))
283+
const BASE_DATE_1904 = new Date(Date.UTC(1904, 0, 1))
284+
const MS_PER_DAY = 24 * 60 * 60 * 1000 // hours * minutes * seconds * milliseconds
285+
286+
/**
287+
* Extracts a date from a serial number in Lotus 1-2-3 date representation.
288+
*/
289+
function extractDateFromSerialNumber(serialNumber: number) {
290+
// Use 1900 system by default, but if date is before 1904,
291+
// switch to 1904 system
292+
let baseDate = BASE_DATE_1900
293+
const date1900 = new Date(BASE_DATE_1900.getTime() + serialNumber * MS_PER_DAY)
294+
295+
if (date1900 < BASE_DATE_1904) {
296+
baseDate = BASE_DATE_1904
297+
}
298+
299+
const wholeDays = Math.floor(serialNumber)
300+
const fractionalDay = serialNumber - wholeDays
301+
const milliseconds = Math.round(fractionalDay * MS_PER_DAY)
302+
303+
return new Date(baseDate.getTime() + wholeDays * MS_PER_DAY + milliseconds)
304+
}
305+
282306
function getFieldValue(fieldType: CollectionFieldType, cellValue: CellValue) {
283307
switch (fieldType) {
284308
case "number": {
@@ -293,9 +317,9 @@ function getFieldValue(fieldType: CollectionFieldType, cellValue: CellValue) {
293317
return CELL_BOOLEAN_VALUES.includes(cellValue)
294318
}
295319
case "date": {
296-
if (typeof cellValue !== "string") return null
320+
if (typeof cellValue !== "number") return null
297321
try {
298-
const date = new Date(Date.parse(cellValue))
322+
const date = extractDateFromSerialNumber(cellValue)
299323
return date.toISOString()
300324
} catch {
301325
return null

0 commit comments

Comments
 (0)