GO excelize 读取excel进行时间类型转换(自动转换)
GO excelize 读取excel进行时间类型转换(自动转换)
需求分析
需求:如何自动识别excel中的时间类型数据并转化成对应的 "Y-m-d H:i:s"类型数据。
分析:excelize在读取excel时,GetRows() 返回的都是字符串类型,并且有些时间类型的数据会进行转换,如果全部转化成 float64 格式,然后转换成对应的字符串,并且excelize提供函数
func ExcelDateToTime(excelDate float64, use1904Format bool) (time.Time, error) {...
}
可以将float64 转换成time.Time 类型,time.Time 很容易转化成对应"Y-m-d H:i:s"格式字符串类型数据。所以我们的难点就在于如何自动识别excel中时日期时间类型数据
excel 单元格格式
以下3月1日数据写入到excel中,excel都会识别成2024/3/1,但是对应单元格格式不同,转化为常规类型的话大部分都相同
- 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 }
结果打印
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
由此我们可以看出时间类型打印出来的内容不尽相同,这里我们可以想办法把他们全部转化成45352
这里我们就把他们转化成常规类型,常规类型的数据大部分都为45352func 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}//转化为常规类型,对应style NumFmt 0styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0})//示例例中都放入A列,所以将A列数据全部转化成对应的常规类型_ = 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 }
再次进行打印
0 - 0 : 45352 1 - 0 : 45352 2 - 0 : 45352 3 - 0 : 45352 4 - 0 : 45352 5 - 0 : 2024-03-01 00:00:00
这时我们就可以看到大部分数据都已经转化成了45352,所以接下来很简单将数据转化成float64类型,再转化成time.time类型,最后转化成我们想要的数据类型
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 说明无法转化成float64 那么有可能本身是字符串时间进行返回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 }
打印结果
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
此时可以解决了我们的问题,指定对应的列,转化为常规类型,然后再转化为float64(针对无法转化的数据返回),再转化为time.time类型,最后转化成我们需要的类型
进阶
那么如何自动进行转化?
其实我们可以根据单元格自定义类型来进行转化,正如上面的例子,如当单元格自定义类型为:
- yyyy"年"m"月"d"日"
- yyyy/m/d
- mmm-yy
- yyyy"年"m"月"
- yyyy/m/d h:mm
- ...
等类型的时候我们需要将他们转化成常规类型,然后根据后续操作转化成我们想要的类型。
如何自定类型判断呢?
其实根据上述转化成常规类型的操作我们就可以知道是哪个字段来进行确定单元格格式类型
styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0})
Style 中的 NumFmt 来进行决定
我们可以看下excelize 中 针对 NumFmt 的注释(在NewStyle方法上面有详细注释),这里我只粘贴部分注释代码
// 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 对应的值代表着各种单元格格式,此时我们可以整理一下我们需要将其转化成常规类型的数据(只做参考,并没有全部实验,自己可以把常用的实验一下)
var ConversionTimeNumFmt = []int{14, //m/d/yy15, //d-mmm-yy17, //mmm-yy22, //m/d/yy h:mm27, // yyyy"年"m"月"30, //m-d-yy31, //yyyy"年"m"月"d"日"36, //yyyy"年"m"月50, //yyyy"年"m"月52, //yyyy"年"m"月57, //yyyy"年"m"月 }
好了,现在我们要转换的单元格格式了,那么接下来我们就可以遍历一下excel的全部单元格格式类型,看一下哪些字段需要进行转化了,不过接下来问题又来了,如何知道excel里面对应的单元格格式呢
如何知道excel中单元格格式
excelize 中提供方法 GetCellStyle() 可以获取该单元格的所有样式对应的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) {... }
根据styleId 我们可以找到对应的所有样式配置 GetStyle()
// GetStyle provides a function to get style definition by given style index. func (f *File) GetStyle(idx int) (*Style, error) {... }
单元格格式 就对应的是 style.NumFmt
这时我们只需要知道每一个单元格的styleId 对应的 style.NumFmt 就可以将数据进行转化(这里做了三个循环,第一遍是获取了对应styleId, 第二遍是更改表样式,将指定类型转化为常规类型,第三遍就是获取对应的数据)
// 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 数组styles := make([]int, 0)//所有需要更改单元格格式的 styleId 数组needChangeStyleIds := make([]int, 0)//所更改的cellsneedChangeCells := make([]string, 0)if len(rows) > 0 {//需要转化成的 style 对应style IdstyleIdZero, _ := 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 说明无法转化成float64 那么有可能本身是字符串时间进行返回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/yy15, //d-mmm-yy17, //mmm-yy22, //m/d/yy h:mm27, // yyyy"年"m"月"30, //m-d-yy31, //yyyy"年"m"月"d"日"36, //yyyy"年"m"月50, //yyyy"年"m"月52, //yyyy"年"m"月57, //yyyy"年"m"月 }
其中InArray方法为
// InArray 判断元素是否再数组内 func InArray[T int | float64 | string](array []T, value T) bool {for _, v := range array {if v == value {return true}}return false }
通过三次遍历操作,自动转化了时间类型
相关文章:
GO excelize 读取excel进行时间类型转换(自动转换)
GO excelize 读取excel进行时间类型转换(自动转换) 需求分析 需求:如何自动识别excel中的时间类型数据并转化成对应的 "Y-m-d H:i:s"类型数据。 分析:excelize在读取excel时,GetRows() 返回的都是字符串类…...

【算法与数据结构】二分查找思想
#1024程序员节|征文# 正文: 二分查找(binary search)是一种基于分治策略的高效搜索算法。它利用数据的有序性,每轮缩小一半搜索范围,直至找到目标元素或搜索区间为空为止,其实有时候数据没有序…...
PHP PDO:安全、灵活的数据持久层解决方案
PHP PDO:安全、灵活的数据持久层解决方案 PHP PDO(PHP Data Objects)是一个轻量级的、具有兼容接口的数据持久层抽象层。它提供了一个统一的API来访问多种数据库系统,如MySQL、PostgreSQL、SQLite、Oracle等。PDO扩展在PHP 5.1.0…...
九、Linux实战案例:项目部署全流程深度解析
Linux实战案例:项目部署全流程深度解析 在当今信息技术领域,Linux服务器凭借其卓越的稳定性、安全性以及强大的性能表现,被广泛应用于各类项目部署场景之中。本文将全面深入地介绍如何将一个项目成功部署至Linux服务器的完整流程,…...

GIS常见前端开发框架
#1024程序员节|征文# 伴随GIS的发展,陆续出现了众多开源地图框架,这些地图框架与众多行业应用融合,极大地拓展了GIS的生命力,这里介绍几个常见的GIS前端开发框架,排名不分先后。 1.Leaflet https://leafl…...

Java | Leetcode Java题解之第506题相对名次
题目: 题解: class Solution {public String[] findRelativeRanks(int[] score) {int n score.length;String[] desc {"Gold Medal", "Silver Medal", "Bronze Medal"};int[][] arr new int[n][2];for (int i 0; i &…...

数据结构 - 堆
今天我们将学习新的数据结构-堆。 01定义 堆是一种特殊的二叉树,并且满足以下两个特性: (1)堆是一棵完全二叉树; (2)堆中任意一个节点元素值都小于等于(或大于等于)左…...

html----图片按钮,商品展示
源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>图标</title><style>.box{width:…...
YOLOv11改进策略【卷积层】| ECCV-2024 小波卷积WTConv 增大感受野,降低参数量计算量,独家创新助力涨点
一、本文介绍 本文记录的是利用小波卷积WTConv模块优化YOLOv11的目标检测网络模型。WTConv的目的是在不出现过参数化的情况下有效地增加卷积的感受野,从而解决了CNN在感受野扩展中的参数膨胀问题。本文将其加入到深度可分离卷积中,有效降低模型参数量和计算量,并二次创新C3…...
redis高级篇之redis源码分析List类型quicklist底层演变 答疑159节
(1)ziplist压缩配置:list-compress-depth 0 表示一个quicklist两端不被压缩的节点个数。这里的节点是指quicklist双向链表的节点,而不是指ziplist里面的数据项个数参数list-compress-depth的取值含义如下: 0:是个特殊值,表示都不压缩。这是Redis的默认值…...
Elasticsearch 与 Lucene 的区别和联系
Elasticsearch 与 Lucene 的区别和联系 Elasticsearch 与 Lucene 的区别和联系一、知识背景Elasticsearch 简介Lucene 简介 二、Elasticsearch 和 Lucene 的区别适用场景性能优势和劣势架构设计的异同点 三、Elasticsearch和Lucene的联系四、Elasticsearch和Lucene的应用案例及…...

OpenCV视觉分析之运动分析(5)背景减除类BackgroundSubtractorMOG2的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 基于高斯混合模型的背景/前景分割算法。 该类实现了在文献[320]和[319]中描述的高斯混合模型背景减除。 cv::BackgroundSubtractorMOG2 类是 O…...

【SAP Hana】X-DOC:数据仓库ETL如何抽取SAP中的CDS视图数据
【SAP Hana】X-DOC:数据仓库ETL如何抽取SAP中的CDS视图数据 1、无参CDS对应数据库视图2、有参CDS对应数据库表函数3、封装有参CDS为无参CDS,从而对应数据库视图 1、无参CDS对应数据库视图 select * from ZFCML_REP_V where mandt 300;2、有参CDS对应数…...
WPF的UpdateSourceTrigger属性
在WPF中,UpdateSourceTrigger属性用于控制数据绑定中何时将绑定目标(通常是UI元素)的值更新回绑定源(通常是数据对象)。这个属性有以下几个值: Default:这是默认值,对于不同的绑定目…...
2024-09-25 环境变量,进程地址空间
一、认识常见的环境变量 1. echo $HOME 输出当前用户对应的家目录 当用户登录系统时,流程如下: (1)用户登录系统后,系统启动Shell程序。 (2)启动bash shell,准备接收用户指令。 &a…...

中国移动机器人将投入养老场景;华为与APUS共筑AI医疗多场景应用
AgeTech News 一周行业大事件 华为与APUS合作,共筑AI医疗多场景应用 中国移动展出人形机器人,预计投入养老等场景 作为科技与奥富能签约,共拓智能适老化改造领域 天与养老与香港科技园,共探智慧养老新模式 中山大学合作中国…...
青少年编程能力等级测评CPA C++ 四级试卷(1)
青少年编程能力等级测评CPA C 四级试卷(1) 一、单项选择题(共15题,每题3分,共45分) CP4_1_1.在面向对象程序设计中,与数据构成一个相互依存的整体的是( )。 A. 对数据…...

树上任意两点的距离
题目描述 给出 n 个点的一棵树,多次询问两点之间的最短距离。 注意:边是双向的。 输入描述 第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数; 下来 n−1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间…...

【 thinkphp8 】00008 thinkphp8数据查询,常用table,name方法,进行数据查询汇总
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【 t…...
Git的命令合集
关于Git的一些命令合集,会慢慢更新! 20241024程序员节开始写的,记录一下~~ git查看log、查看详细提交记录 会显示之前的提交记录 , 排序由近及远 git log log按q退出 git回退到某个commit命令: 退到/进到指定commit的sha码&…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...
数据库正常,但后端收不到数据原因及解决
从代码和日志来看,后端SQL查询确实返回了数据,但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离,并且ai辅助开发的时候,很容易出现前后端变量名不一致情况,还不报错,只是单…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
【Ftrace 专栏】Ftrace 参考博文
ftrace、perf、bcc、bpftrace、ply、simple_perf的使用Ftrace 基本用法Linux 利用 ftrace 分析内核调用如何利用ftrace精确跟踪特定进程调度信息使用 ftrace 进行追踪延迟Linux-培训笔记-ftracehttps://www.kernel.org/doc/html/v4.18/trace/events.htmlhttps://blog.csdn.net/…...