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

    这里我们就把他们转化成常规类型,常规类型的数据大部分都为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}//转化为常规类型,对应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
    }
    

    通过三次遍历操作,自动转化了时间类型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/455754.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

轻松掌握Win10录屏技巧:四大神器推荐!

在Win10系统中,录屏功能的应用越来越广泛,无论是用于工作演示、在线教学还是游戏分享,一款好用的录屏软件都是必不可少的。今天,我们将推荐四款录屏工具! 福昕录屏大师 直达链接:www.foxitsoftware.cn/RE…

iOS--利用UITableViewDataSourcePrefetching实现平滑如丝的无限滚动

前言: 相信大家在网络不好的时候使用列表分页的App会获得非常不好的体验,由于网络的问题,会有明显的卡顿,就像抖音等App,那么我们是否能使用一些手段来优化这个体验呢?这里可以用到UITableView中另一个协议…

Linux Debian12基于ImageMagick图像处理工具编写shell脚本用于常见图片png、jpg、jpeg、tiff格式批量转webp格式

在Linux系统中,使用ImageMagick可以图片格式转换,其中最常用的是通过命令行工具进行。 ImageMagick是一个非常强大的图像处理工具集,它包含了许多用于图像转换的命令。 一、安装ImageMagick(如果尚未安装)&#xff1…

蓝牙资讯|iOS 18.1 正式版下周推送,AirPods Pro 2耳机将带来助听器功能

苹果公司宣布将在下周发布 iOS 18.1 正式版,同时确认该更新将为 AirPods Pro 2 耳机带来新增“临床级”助听器功能。在启用功能后,用户首先需要使用 AirPods 和 iPhone 进行简短的听力测试,如果检测到听力损失,系统将创建一项“个…

MoCoOp: Mixture of Prompt Learning for Vision Language Models

文章汇总 当前的问题 1)数据集风格变化。 如图1所示,对于一个数据集,单个软提示可能不足以捕获数据中呈现的各种样式。同一数据集中的不同实例可能与不同的提示符兼容。因此,更**自然的做法是使用多个提示来充分表示这些变化**。 2)过拟合…

101. UE5 GAS RPG 实现范围技能奥术爆发表现

在上一篇文章里,我们实现了范围技能的范围指示功能。范围指示是在释放技能前,确认技能的攻击范围,在确认位置后,通过额外按键进行触发技能释放。 在这一篇里,我们将先实现在技能里使用范围指示,并能够播放对…

硬件基础知识补全计划【一】电阻

一、电阻理论 1.1 电流定义 电流:电流的强弱用电流强度来描述,电流强度是单位时间内通过导体某一横截面的电荷量,简称电流,用I表示。1 秒内有 6.241509310^18 个元电荷通过横截面的电流,定义为 1 安 (A)。 电压&…

Redis Search系列 - 第四讲 支持中文

目录 一、支持中文二、自定义中文词典2.1 Redis Search设置FRISOINI参数2.2 friso.ini文件相关配置1)自定义friso UTF-8字典2)修改friso.ini配置文件 三、实测中文分词效果 一、支持中文 Redis Stack 从版本 0.99.0 开始支持中文文档的添加和分词。中文…

MoeCTF 2024 ---Misc方向WP

安全杂项 signin 题目描述: xdsec的小伙伴们和参赛者来上课,碰巧这一天签到系统坏了,作为老师的你,要帮他们 教师代签。 特殊提醒:luo同学今天好像在宿舍打游戏,不想来上课,这是严重的缺勤行为…

PoissonRecon学习笔记

1. Screened Poisson Reconstruction (SPR) 源码:https://github.com/mkazhdan/PoissonRecon However, as noted by several researchers, it suffers from a tendency to over-smooth the data. 泊松重建存在过度平滑的现象。 方法:position and gradi…

【QT】QChart绘制曲线与散点图

功能描述:绘制曲线和散点图,添加图例信息,可以进行缩放、移动,鼠标在曲线上时显示当前坐标点 QChart功能类 继承QGraphicsView 重写鼠标事件函数 protected:void resizeEvent(QResizeEvent *event);void mouseMoveEvent(QMouseEvent *event);void mousePressEvent(QMouseEv…

C++共同体

共同体是一种数据格式,他能储存不同的数据类型,但是同一时间只能储存其中的一种类型。 语法: union 共同体名 { 成员一的数据类型 成员名一; 成员二的数据类型 成员名二; 成员n的数据类型 成员名n; }

PHP养老院管理系统-计算机设计毕业源码-00115

摘要 随着社会老龄化进程的加速,养老院管理系统在提高养老服务质量和效率方面发挥着越来越重要的作用。本研究旨在设计和实现一个基于PHP的养老院管理系统,以满足养老院的日常管理需求,提升养老服务水平。 本研究首先对养老院管理系统的需求进…

大模型系列——幻觉

在kimi中输入提示词,得到本文脉络: 我想写大模型幻觉技术文章,请对以下标题进行补全和细化: 1、幻觉原因 2、幻觉消除方案 3、幻觉检测方案 4、幻觉评估数据集 背景 研究人员将大模型的幻觉分为事实性幻觉(Factuali…

【状态机DP】力扣2786. 访问数组中的位置使分数最大

给你一个下标从 0 开始的整数数组 nums 和一个正整数 x 。 你 一开始 在数组的位置 0 处&#xff0c;你可以按照下述规则访问数组中的其他位置&#xff1a; 如果你当前在位置 i &#xff0c;那么你可以移动到满足 i < j 的 任意 位置 j 。 对于你访问的位置 i &#xff0c…

系统架构图设计(轻量级架构)

轻量级架构一般包括&#xff1a;表现层、业务逻辑层、持久层、数据库层 表现层架构 MVC 模型&#xff08;Model&#xff09;&#xff1a;应用程序的主体部分&#xff0c;表示业务数据和业务逻辑视图&#xff08;View&#xff09;&#xff1a;用户看到并与之交流的界面控制器&…

Windows 11优化利器:全方位定制你的操作系统

最近&#xff0c;有用户询问如何禁用Windows Defender&#xff0c;这让我想起了一款功能强大的Windows 11设置工具。这款工具不仅包含了禁用Defender的功能&#xff0c;还提供了许多其他实用的系统定制选项。 工具概览 这款名为“Windows11轻松设置”的软件&#xff0c;最近进…

延迟队列实现及其原理详解

1.绪论 本文主要讲解常见的几种延迟队列的实现方式&#xff0c;以及其原理。 2.延迟队列的使用场景 延迟队列主要用于解决每个被调度的任务开始执行的时间不一致的场景&#xff0c;主要包含如下场景: 1.比如订单超过15分钟后&#xff0c;关闭未关闭的订单。 2.比如用户可以…

保姆级教程来喽!从下载开始的Luatools~小白必看!

对于刚接触Luatools的新手朋友们&#xff0c;这篇保姆级教程将手把手教你如何从下载开始使用这款强大的调试工具。Luatools适用于合宙的多种4G模组&#xff0c;支持固件获取、打包、调试等多项功能&#xff0c;确保你的开发工作事半功倍。 本文就来讲解一下Luatools的下载和使…

Flask集成sqlalchemy (学习笔记)

文章目录 前言一、安装sqlalchemy二、连接mysql1.创建一个配置数据库信息的文件&#xff08;如上图&#xff09;2.创建sqlalchemy配置文件3.app.py中引入注册4.创建模型对象5.在app.py中进行关联6.执行映射语句&#xff08;迁移命令&#xff09; 总结 前言 本文章讲解的是分模…