主动做事的收益或许不会在一两天 内显现出来,但是长期坚持下来,主动做事的人就能和其他人拉开距离。到了关键的时候,只有主动做事,奇迹才会发生。--- 吴军 《格局》
大家好,我是渔夫子。「Go学堂」新推出“Go工具箱”系列,意在给大家分享使用go语言编写的、实用的、好玩的工具。
今天给大家推荐一个轻量级、语义化、对开发者友好的 golang 时间处理库:carbon。该库已被awesome-go库收录。
carbon小档案
carbon小档案 | |||
---|---|---|---|
star | 595 | used by | 198 |
contributors | 22 | 作者 | guogouyin |
功能简介 | 一个轻量级、语义化、对开发者友好的 golang 时间处理库。该库支持链式调用和gorm、xorm等主流orm | ||
项目地址 | https://github.com/golang-module/carbon | ||
相关知识 | time.Time、json自定义格式化 |
一、安装
当go 版本 ≥1.16 时,推荐使用v2包,如下:
代码语言:javascript复制 go get -u github.com/golang-module/carbon/v2
go 小于1.16 时,必须使用第一个版本
代码语言:javascript复制go get -u github.com/golang-module/carbon
二、carbon使用及实现原理
在Go的标准库中,日期的处理是基于time.Time结构体的。而carbon包也是基于time.Time结构体,并对一些常用的行为进行了封装,提高了对时间处理的效率以及代码的可读性。所以在carbon包中所有的行为是基于Carbon结构体的。下面是carbon结构体的数据结构
Carbon结构体很简单,共5个字段。由各字段可知该包能够处理日期和时间、设置时区、国际化支持以及错误处理。其主要功能如下图所示:
carbon功能体系精简版.jpg
在carbon的项目主页对各种功能的使用已经说的非常详细了,这里就不再重复介绍。接下来我们会通过两个示例来说明carbon的具体应用。结构体中的时间字段转json时的时间格式和计算两个日期相差几个自然天。
示例一:结构体中的时间字段转json
这里主要是想说明在对time.Time的字段进行json格式化时如何自定义日期输出的格式。因为time.Time类型的字段默认是按RFC3339标准格式输出的,即 “2022-08-08T12:12:12 08:00”这种格式。我们先来看一个示例:
代码语言:javascript复制type Person struct {
Name string
Birthday time.Time
}
birthday := carbon.Parse("2022-08-08 12:12:12").Carbon2Time()
p:= Person{
Name: "渔夫子",
Birthday: birthday,
}
encodeToJson, _ := json.Marshal(p) //输出{"Name":"渔夫子","Birthday":"2022-08-08T12:12:12 08:00"}
这里最终输出的json字符串如下:
代码语言:javascript复制{
"Name":"渔夫子",
"Birthday":"2022-08-08T12:12:12 08:00"
}
Birthday字段输出的日期格式是“2022-08-08T12:12:12 08:00”,原因在于在json包中定义了一个Marshaler接口,数据类型只要实现了该接口,那么就优先使用该类型自定义的MarshalJSON方法。如下:
代码语言:javascript复制type Marshaler interface {
MarshalJSON() ([]byte, error)
}
time.Time类型就是实现了该接口,并且在具体的实现中采用了RFC3339标准的日期格式。所以才看到我们的输出是"2022-08-08T12:12:12 08:00"。我们看下time.Time类型中MarshalJson方法:
代码语言:javascript复制func (t Time) MarshalJSON() ([]byte, error) {
if y := t.Year(); y < 0 || y >= 10000 {
// RFC 3339 is clear that years are 4 digits exactly.
// See golang.org/issue/4556#c15 for more discussion.
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
}
b := make([]byte, 0, len(RFC3339Nano) 2)
b = append(b, '"')
b = t.AppendFormat(b, RFC3339Nano)
b = append(b, '"')
return b, nil
}
显然,这种日期的格式是不符合我们平时的阅读习惯的。我们的习惯是"2022-08-08 12:12:12"这种格式就好。那怎么实现呢?那就是自定义一种类型,并且这种类型实现json包中的Marshaler接口。
carbon中就已经帮我们做了这些事情。我们看carbon中的DateTime类型。
代码语言:javascript复制type Person struct {
Name string
Birthday carbon.DateTime
}
birthday := carbon.DateTime{
Carbon:carbon.Parse("2022-08-08 12:12:12")
}
p:= Person{
Name: "渔夫子",
Birthday: birthday,
}
encodeToJson, _ := json.Marshal(p) //输出{"Name":"渔夫子","Birthday":"2022-08-08 12:12:12"}
我们看到最终json串中的Birthday字段输出是“2022-08-08 12:12:12"的格式了。这是因为carbon.DateTime类型也实现了json包中的Marshaler接口,在MarshalJSON的实现方法中让time.Time字段按"2006-01-02 15:04:05"这种格式输出。当然在carbon中还有其他的实现了json包中的Marshaler接口,大家自行查看即可。
示例二:计算两个日期相差几个自然日
以北京时间为例,给定开始时间2022-10-31 21:23:45,作为第1个自然日。那么日期2022-11-01 14:23:45相对于开始时间就是第2个自然日。这里需注意的是 只要到了2022-11-01日,就算作第2个自然日了。
那么,给定任意两个日期,用程序该怎么计算呢?我的方法是以开始日期的00:00:00作为起点,以结束日期的23:59:59秒再加1秒作为终点,计算终点和起点的时间差,然后再除以一天的秒数86400,得出来的商就是结束日期相对于开始日期的第几个自然日。
一天的结束是在23:59:59,再加1秒实际就到了次日的00:00:00,这样做是为了得到86400(一天总共有86400秒)的整数倍。
carbon时间计算2.jpg
所以我们这里就要利用carbon中获取一天开始时间和结束时间相关的函数了。代码如下:
代码语言:javascript复制startDate := "2022-10-31 04:13:14"
endDate := "2022-11-02 01:13:14"
startDateUnix := carbon.Parse(startDate).StartOfDay().Timestamp()
endDateUnix := carbon.Parse(endDate).EndOfDay().AddSeconds(1).Timestamp()
days := (endDateUnix - startDateUnix) / 86400
StartOfDay和EndOfDay的实现,本质上还是利用了time.Date函数。一天的开始就是指定时分秒时都为0,一天的结束就是指定时分秒时为23点,59分,59秒。如下:
代码语言:javascript复制locat, error:= time.LoadLocation("Asia/Shanghai")
// 初始化成一天的开始
time.Date(2022, time.Month(10), 31, 0, 0, 0, 0, locat)
// 初始化成一天的结束
time.Date(2022, time.Month(11), 1, 23, 59, 59, 0, locat)
关于更多time.Time时间的处理大家可以参考我之前的文章:golang中time包使用教程之基础使用篇
---特别推荐---
特别推荐:一个专注go项目实战、项目中踩坑经验及避坑指南、各种好玩的go工具的公众号。「Go学堂」,专注实用性,非常值得大家关注。点击下方公众号卡片,直接关注。关注送《100个go常见的错误》pdf文档。