如何解析你,Excel 的 Date 呀

ThinkJS發表於2018-12-06

簡單的背景介紹

不久前,我們接了一個自己做前端後端產品的活,從此過上了可憐巴巴敲程式碼開開心心收穫知識的日子呢。

那是一個平平無奇的週一下午

使用者小姐姐在群裡說,系統篩選工卡有效期不好使。(系統:不不不,不是我的鍋

我看了一下資料庫,發現,我們原定的有效期格式是這樣的

如何解析你,Excel 的 Date 呀

整整齊齊。

資料庫中當時的資料是這樣的

如何解析你,Excel 的 Date 呀

甚至是這樣的

如何解析你,Excel 的 Date 呀

看到這種情況,我覺得肯定是輸入的時候輸的不太對(年輕…

於是我決定從 Excel 下手

小姐姐們的操作流程是先用我們的系統匯出一份 Excel,編輯之後再匯入系統的,那隻要我把這工卡一列的格式限制為日期,就一定可以統一格式的,嗯。

我們專案使用了 js-xlsx 處理表格的匯入匯出,下面是匯出 Excel 的虛擬碼:

import * as XLSX from 'xlsx';

const xlsxMineType = 'application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet'
const data = 資料.map((s: any) => ({
ID: s.id,
工卡有效期: s.card_expired,
……
}));

const sheet = XLSX.utils.json_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, sheet, '員工資訊表');
const wbbuf = XLSX.write(wb, {
  type: 'base64'
});

this.success({ name: "員工資訊表.xlsx", data: wbbuf, type:xlsxMineType });
複製程式碼

通過 json_to_sheet 可以拿到包含單元格資訊的物件

{ 
 A2: { t: 'n', v: 3776 },
 B2: { t: 's', v: '2019-04-01' },
 A3: { t: 'n', v: 3831 },
 B3: { t: 's', v: '2019-04-01' },
 A1: { t: 's', v: 'ID' },
 B1: { t: 's', v: '工卡有效期' },
 '!ref': 'A1:B3' 
}
複製程式碼

物件中以單元格位置作為 key,每個單元格的值(v)、型別(t)等等屬性作為 value。其中單元格的型別支援:

b Boolean, n Number, e error, s String, d Date

看起來 Date 型別十分符合上面的要求,就嘗試了一下:

const sheet = XLSX.utils.json_to_sheet(data);
// 篩選出除表頭的工卡列
Object.keys(sheet).filter(item => /^B/.test(item) && item !== "B1").forEach(key => {
   sheet[key].t = "d";
})
複製程式碼

如何解析你,Excel 的 Date 呀

然鵝,如果工卡有效期本來就為空,這時候匯出,開啟 Excel 會報錯,並且空的位置會變成 NaN

如何解析你,Excel 的 Date 呀

翻閱了各種中英文文件、Issue,匯出一百多個員工資訊表之後,我發現 Excel 真的很奇妙,或許應該在 js 上來格式化匯入的資料,而不是限制單元格的型別。

如果不控制單元格型別的話,那麼當管理員輸入日期的時候,這個單元格可能是:文字、常規、日期、自定義型別,所以只要保證不管單元格是什麼格式,程式都能拿到正確的資料就好了。

當管理員使的工卡有效期的單元格型別是文字或者常規的時候,則比較簡單,程式可以按預期解析出來一個相應的字串,用 moment 解析一下,就可以獲得想要的格式的資料了。

那麼當有效期單元格的型別是日期和自定義的時候,我們拿到的資料是像下圖一樣

如何解析你,Excel 的 Date 呀

這也就是之前資料庫中奇怪的數字的由來,這個數字的意義,其實是當前日期距離 1900 年 1月 0 日的天數。還需要注意的是,Excel 中有個 bug:

如何解析你,Excel 的 Date 呀

它以為 1900 年是閏年,所以我們拿到的天數都會多了一天,因為轉換之前還需要先進行減一操作…

item.工卡有效期 = new Date(1900, 0, expried - 1)
複製程式碼

這樣之後就可以拿到正確的日期啦。咕嘰。

相關文章