GO excelize讀取excel進(jìn)行時間類型轉(zhuǎn)換的示例代碼(自動轉(zhuǎn)換)
需求分析
需求:如何自動識別excel中的時間類型數(shù)據(jù)并轉(zhuǎn)化成對應(yīng)的 "Y-m-d H:i:s"類型數(shù)據(jù)。
分析:excelize在讀取excel時,GetRows() 返回的都是字符串類型,并且有些時間類型的數(shù)據(jù)會進(jìn)行轉(zhuǎn)換,如果全部轉(zhuǎn)化成 float64 格式,然后轉(zhuǎn)換成對應(yīng)的字符串,并且excelize提供函數(shù)
func ExcelDateToTime(excelDate float64, use1904Format bool) (time.Time, error) { ... }
可以將float64 轉(zhuǎn)換成time.Time 類型,time.Time 很容易轉(zhuǎn)化成對應(yīng)"Y-m-d H:i:s"格式字符串類型數(shù)據(jù)。所以我們的難點就在于如何自動識別excel中時日期時間類型數(shù)據(jù)
excel 單元格格式
以下3月1日數(shù)據(jù)寫入到excel中,excel都會識別成2024/3/1,但是對應(yīng)單元格格式不同,轉(zhuǎn)化為常規(guī)類型的話大部分都相同
- 2024年3月1日------------- yyyy"年"m"月"d"日"-------------453352
- 2024/3/1------------- yyyy/m/d-------------453352
- Mar-24------------- mmm-yy-------------453352
- 2024年3月------------- yyyy"年"m"月"-------------453352
- 2024/3/1 0:00-------------yyyy/m/d h:mm-------------453352
- '2024-03-01 00:00:00-------------通用-------------2024-03-01 00:00:00
excelize 讀取
func parseFileUrl(filePath string) ([]map[string]string, error) { f, err := excelize.OpenFile(filePath) if err != nil { return nil, err } sheetName := f.GetSheetName(0) rows, err := f.GetRows(sheetName) if err != nil { return nil, err } if len(rows) > 0 { for rowKey, cols := range rows { if len(cols) > 0 { for colKey, value := range cols { fmt.Println(rowKey, "-", colKey, ":", value) } } } } return nil, err }
結(jié)果打印
0 - 0 : 45352 1 - 0 : 03-01-24 2 - 0 : Mar-24 3 - 0 : 45352 4 - 0 : 3/1/24 00:00 5 - 0 : 2024-03-01 00:00:00
由此我們可以看出時間類型打印出來的內(nèi)容不盡相同,這里我們可以想辦法把他們?nèi)哭D(zhuǎn)化成45352
這里我們就把他們轉(zhuǎn)化成常規(guī)類型,常規(guī)類型的數(shù)據(jù)大部分都為45352
func parseFileUrl(filePath string) ([]map[string]string, error) { f, err := excelize.OpenFile(filePath) if err != nil { return nil, err } sheetName := f.GetSheetName(0) rows, err := f.GetRows(sheetName) if err != nil { return nil, err } //轉(zhuǎn)化為常規(guī)類型,對應(yīng)style NumFmt 0 styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0}) //示例例中都放入A列,所以將A列數(shù)據(jù)全部轉(zhuǎn)化成對應(yīng)的常規(guī)類型 _ = f.SetColStyle(sheetName, "A", styleId) rows, err = f.GetRows(sheetName) if len(rows) > 0 { for rowKey, cols := range rows { if len(cols) > 0 { for colKey, value := range cols { fmt.Println(rowKey, "-", colKey, ":", value) } } } } return nil, err }
再次進(jìn)行打印
0 - 0 : 45352 1 - 0 : 45352 2 - 0 : 45352 3 - 0 : 45352 4 - 0 : 45352 5 - 0 : 2024-03-01 00:00:00
這時我們就可以看到大部分?jǐn)?shù)據(jù)都已經(jīng)轉(zhuǎn)化成了45352,所以接下來很簡單將數(shù)據(jù)轉(zhuǎn)化成float64類型,再轉(zhuǎn)化成time.time類型,最后轉(zhuǎn)化成我們想要的數(shù)據(jù)類型
func parseFileUrl(filePath string) ([]map[string]string, error) { f, err := excelize.OpenFile(filePath) if err != nil { return nil, err } sheetName := f.GetSheetName(0) rows, err := f.GetRows(sheetName) if err != nil { return nil, err } styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0}) _ = f.SetColStyle(sheetName, "A", styleId) rows, err = f.GetRows(sheetName) if len(rows) > 0 { for rowKey, cols := range rows { if len(cols) > 0 { for colKey, value := range cols { timeFloat, err := strconv.ParseFloat(value, 64) if err != nil { //err 說明無法轉(zhuǎn)化成float64 那么有可能本身是字符串時間進(jìn)行返回 timeTime, err := time.Parse("2006-01-02 15:04:05", value) if err != nil { fmt.Println(rowKey, "-", colKey, ":", value) } else { value = timeTime.Format("2006-01-02 15:04:05") fmt.Println(rowKey, "-", colKey, ":", value) } break } timeTime, _ := excelize.ExcelDateToTime(timeFloat, false) value = timeTime.Format("2006-01-02 15:04:05") fmt.Println(rowKey, "-", colKey, ":", value) } } } } return nil, err }
打印結(jié)果
0 - 0 : 2024-03-01 00:00:00 1 - 0 : 2024-03-01 00:00:00 2 - 0 : 2024-03-01 00:00:00 3 - 0 : 2024-03-01 00:00:00 4 - 0 : 2024-03-01 00:00:00 5 - 0 : 2024-03-01 00:00:00
此時可以解決了我們的問題,指定對應(yīng)的列,轉(zhuǎn)化為常規(guī)類型,然后再轉(zhuǎn)化為float64(針對無法轉(zhuǎn)化的數(shù)據(jù)返回),再轉(zhuǎn)化為time.time類型,最后轉(zhuǎn)化成我們需要的類型
進(jìn)階
那么如何自動進(jìn)行轉(zhuǎn)化?
其實我們可以根據(jù)單元格自定義類型來進(jìn)行轉(zhuǎn)化,正如上面的例子,如當(dāng)單元格自定義類型為:
- yyyy"年"m"月"d"日"
- yyyy/m/d
- mmm-yy
- yyyy"年"m"月"
- yyyy/m/d h:mm
- ...
- 等類型的時候我們需要將他們轉(zhuǎn)化成常規(guī)類型,然后根據(jù)后續(xù)操作轉(zhuǎn)化成我們想要的類型。
如何自定類型判斷呢?
其實根據(jù)上述轉(zhuǎn)化成常規(guī)類型的操作我們就可以知道是哪個字段來進(jìn)行確定單元格格式類型
styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0})
Style 中的 NumFmt 來進(jìn)行決定
我們可以看下excelize 中 針對 NumFmt 的注釋(在NewStyle方法上面有詳細(xì)注釋),這里我只粘貼部分注釋代碼
// Index | Format String // -------+---------------------------------------------------- // 0 | General // 1 | 0 // 2 | 0.00 // 3 | #,##0 // 4 | #,##0.00 // 5 | ($#,##0_);($#,##0) // 6 | ($#,##0_);[Red]($#,##0) // 7 | ($#,##0.00_);($#,##0.00) // 8 | ($#,##0.00_);[Red]($#,##0.00) // 9 | 0% // 10 | 0.00% // 11 | 0.00E+00 // 12 | # ?/? // 13 | # ??/?? // 14 | m/d/yy // 15 | d-mmm-yy // 16 | d-mmm // 17 | mmm-yy // 18 | h:mm AM/PM // 19 | h:mm:ss AM/PM // 20 | h:mm // 21 | h:mm:ss // 22 | m/d/yy h:mm // ... | ... // 37 | (#,##0_);(#,##0) // 38 | (#,##0_);[Red](#,##0) // 39 | (#,##0.00_);(#,##0.00) // 40 | (#,##0.00_);[Red](#,##0.00) // 41 | _(* #,##0_);_(* (#,##0);_(* "-"_);_(@_) // 42 | _($* #,##0_);_($* (#,##0);_($* "-"_);_(@_) // 43 | _(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_) // 44 | _($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_) // 45 | mm:ss // 46 | [h]:mm:ss // 47 | mm:ss.0 // 48 | ##0.0E+0 // 49 | @ // Number format code in zh-cn language: // // Index | Symbol // -------+------------------------------------------- // 27 | yyyy"年"m"月" // 28 | m"月"d"日" // 29 | m"月"d"日" // 30 | m-d-yy // 31 | yyyy"年"m"月"d"日" // 32 | h"時"mm"分" // 33 | h"時"mm"分"ss"秒" // 34 | 上午/下午 h"時"mm"分" // 35 | 上午/下午 h"時"mm"分"ss"秒 // 36 | yyyy"年"m"月 // 50 | yyyy"年"m"月 // 51 | m"月"d"日 // 52 | yyyy"年"m"月 // 53 | m"月"d"日 // 54 | m"月"d"日 // 55 | 上午/下午 h"時"mm"分 // 56 | 上午/下午 h"時"mm"分"ss"秒 // 57 | yyyy"年"m"月 // 58 | m"月"d"日"
我們可以知道NumFmt 對應(yīng)的值代表著各種單元格格式,此時我們可以整理一下我們需要將其轉(zhuǎn)化成常規(guī)類型的數(shù)據(jù)(只做參考,并沒有全部實驗,自己可以把常用的實驗一下)
var ConversionTimeNumFmt = []int{ 14, //m/d/yy 15, //d-mmm-yy 17, //mmm-yy 22, //m/d/yy h:mm 27, // yyyy"年"m"月" 30, //m-d-yy 31, //yyyy"年"m"月"d"日" 36, //yyyy"年"m"月 50, //yyyy"年"m"月 52, //yyyy"年"m"月 57, //yyyy"年"m"月 }
好了,現(xiàn)在我們要轉(zhuǎn)換的單元格格式了,那么接下來我們就可以遍歷一下excel的全部單元格格式類型,看一下哪些字段需要進(jìn)行轉(zhuǎn)化了,不過接下來問題又來了,如何知道excel里面對應(yīng)的單元格格式呢
如何知道excel中單元格格式
excelize 中提供方法 GetCellStyle() 可以獲取該單元格的所有樣式對應(yīng)的styleId
// GetCellStyle provides a function to get cell style index by given worksheet // name and cell reference. This function is concurrency safe. func (f *File) GetCellStyle(sheet, cell string) (int, error) { ... }
根據(jù)styleId 我們可以找到對應(yīng)的所有樣式配置 GetStyle()
// GetStyle provides a function to get style definition by given style index. func (f *File) GetStyle(idx int) (*Style, error) { ... }
單元格格式 就對應(yīng)的是 style.NumFmt
這時我們只需要知道每一個單元格的styleId 對應(yīng)的 style.NumFmt 就可以將數(shù)據(jù)進(jìn)行轉(zhuǎn)化(這里做了三個循環(huán),第一遍是獲取了對應(yīng)styleId, 第二遍是更改表樣式,將指定類型轉(zhuǎn)化為常規(guī)類型,第三遍就是獲取對應(yīng)的數(shù)據(jù))
// parseFileUrl 解析文件流excel func parseFileUrl(filePath string) ([]map[string]string, error) { f, err := excelize.OpenFile(filePath) if err != nil { return nil, err } sheetName := f.GetSheetName(0) rows, err := f.GetRows(sheetName) if err != nil { return nil, err } //讀取excel 所有styleId 數(shù)組 styles := make([]int, 0) //所有需要更改單元格格式的 styleId 數(shù)組 needChangeStyleIds := make([]int, 0) //所更改的cells needChangeCells := make([]string, 0) if len(rows) > 0 { //需要轉(zhuǎn)化成的 style 對應(yīng)style Id styleIdZero, _ := f.NewStyle(&excelize.Style{NumFmt: 0}) for rowKey, cols := range rows { if len(cols) > 0 { for colKey, _ := range cols { columnNumber, _ := excelize.CoordinatesToCellName(colKey+1, rowKey+1) styleId, _ := f.GetCellStyle(sheetName, columnNumber) if !arrayHelper.InArray(styles, styleId) { styles = append(styles, styleId) } } } } fmt.Println(styles) if len(styles) > 0 { for _, styleId := range styles { style, _ := f.GetStyle(styleId) if arrayHelper.InArray(ConversionTimeNumFmt, style.NumFmt) { needChangeStyleIds = append(needChangeStyleIds, styleId) } } } for rowKey, cols := range rows { if len(cols) > 0 { for colKey, _ := range cols { columnNumber, _ := excelize.CoordinatesToCellName(colKey+1, rowKey+1) styleId, _ := f.GetCellStyle(sheetName, columnNumber) if arrayHelper.InArray(needChangeStyleIds, styleId) { _ = f.SetCellStyle(sheetName, columnNumber, columnNumber, styleIdZero) needChangeCells = append(needChangeCells, columnNumber) } } } } rows, err = f.GetRows(sheetName) if err != nil { return nil, err } if len(rows) > 0 { for rowKey, cols := range rows { if len(cols) > 0 { for colKey, value := range cols { columnNumber, _ := excelize.CoordinatesToCellName(colKey+1, rowKey+1) if arrayHelper.InArray(needChangeCells, columnNumber) { timeFloat, err := strconv.ParseFloat(value, 64) if err != nil { //err 說明無法轉(zhuǎn)化成float64 那么有可能本身是字符串時間進(jìn)行返回 timeTime, err := time.Parse("2006-01-02 15:04:05", value) if err != nil { fmt.Println(rowKey, "-", colKey, ":", value) } else { value = timeTime.Format("2006-01-02 15:04:05") fmt.Println(rowKey, "-", colKey, ":", value) } break } timeTime, _ := excelize.ExcelDateToTime(timeFloat, false) value = timeTime.Format("2006-01-02 15:04:05") fmt.Println(rowKey, "-", colKey, ":", value) } } } } } } return nil, err } var ConversionTimeNumFmt = []int{ 14, //m/d/yy 15, //d-mmm-yy 17, //mmm-yy 22, //m/d/yy h:mm 27, // yyyy"年"m"月" 30, //m-d-yy 31, //yyyy"年"m"月"d"日" 36, //yyyy"年"m"月 50, //yyyy"年"m"月 52, //yyyy"年"m"月 57, //yyyy"年"m"月 }
其中InArray方法為
// InArray 判斷元素是否再數(shù)組內(nèi) func InArray[T int | float64 | string](array []T, value T) bool { for _, v := range array { if v == value { return true } } return false }
通過三次遍歷操作,自動轉(zhuǎn)化了時間類型
以上就是GO excelize讀取excel進(jìn)行時間類型轉(zhuǎn)換的示例代碼(自動轉(zhuǎn)換)的詳細(xì)內(nèi)容,更多關(guān)于GO excelize excel時間類型轉(zhuǎn)換的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go結(jié)構(gòu)體指針引發(fā)的值傳遞思考分析
這篇文章主要為大家介紹了Go結(jié)構(gòu)體指針引發(fā)的值傳遞思考分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12深入了解Golang?interface{}的底層原理實現(xiàn)
在?Go?語言沒有泛型之前,接口可以作為一種替代實現(xiàn),也就是萬物皆為的?interface。那到底?interface?是怎么設(shè)計的底層結(jié)構(gòu)呢?下面咱們透過底層分別看一下這兩種類型的接口原理。感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助2022-10-10一文帶你理解Golang中的Time結(jié)構(gòu)
根據(jù)golang的time包的文檔可以知道,golang的time結(jié)構(gòu)中存儲了兩種時鐘,一種是Wall?Clocks,一種是Monotonic?Clocks,下面我們就來簡單了解一下這兩種結(jié)構(gòu)吧2023-09-09