Go学堂

V1

2022/11/05阅读:15主题:极客黑

「Go工具箱」推荐一个轻量级、语义化的时间处理库:carbon

在工作中,主动性不仅体现在像老黄牛一样把本职工作做好,还要主动和领导沟通,承担更多、更重要的任务。 --- 吴军 《格局》

大家好,我是渔夫子。「Go学堂」新推出“Go工具箱”系列,意在给大家分享使用go语言编写的、实用的、好玩的工具。

今天给大家推荐一个轻量级、语义化、对开发者友好的 golang 时间处理库:carbon

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包,如下:

 go get -u github.com/golang-module/carbon/v2

go 小于1.16 时,必须使用第一个版本

go get -u github.com/golang-module/carbon

二、carbon使用及实现原理

在Go的标准库中,日期的处理是基于time.Time结构体的。而carbon包也是基于time.Time结构体,并对一些常用的行为进行了封装,提高了对时间处理的效率以及代码的可读性。所以在carbon包中所有的行为是基于Carbon结构体的。下面是carbon结构体的数据结构 算法反作弊系统流程图-carbon包.png

Carbon结构体很简单,共5个字段。由各字段可知该包能够处理日期和时间、设置时区、国际化支持以及错误处理。其主要功能如下图所示:

carbon功能体系精简版.jpg
carbon功能体系精简版.jpg

在carbon的项目主页对各种功能的使用已经说的非常详细了,这里就不再重复介绍。接下来我们会通过两个示例来说明carbon的具体应用。结构体中的时间字段转json时的时间格式和计算两个日期相差几个自然天。

示例一:结构体中的时间字段转json

这里主要是想说明在对time.Time的字段进行json格式化时如何自定义日期输出的格式。因为time.Time类型的字段默认是按RFC3339标准格式输出的,即 “2022-08-08T12:12:12+08:00”这种格式。我们先来看一个示例:

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字符串如下:

{
  "Name":"渔夫子",
  "Birthday":"2022-08-08T12:12:12+08:00"
}

Birthday字段输出的日期格式是“2022-08-08T12:12:12+08:00”,原因在于在json包中定义了一个Marshaler接口,数据类型只要实现了该接口,那么就优先使用该类型自定义的MarshalJSON方法。如下:

type Marshaler interface {
 MarshalJSON() ([]byte, error)
}

time.Time类型就是实现了该接口,并且在具体的实现中采用了RFC3339标准的日期格式。所以才看到我们的输出是"2022-08-08T12:12:12+08:00"。我们看下time.Time类型中MarshalJson方法:

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类型。

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时间计算2.jpg

所以我们这里就要利用carbon中获取一天开始时间和结束时间相关的函数了。代码如下:

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秒。如下:

locat, error:= time.LoadLocation("Asia/Shanghai")
// 初始化成一天的开始
time.Date(2022, time.Month(10), 310000, locat)

// 初始化成一天的结束
time.Date(2022, time.Month(11), 12359590, locat)

关于更多time.Time时间的处理大家可以参考我之前的文章:

---特别推荐--- 特别推荐:一个专注go项目实战、项目中踩坑经验及避坑指南、各种好玩的go工具的公众号。「Go学堂」,专注实用性,非常值得大家关注。点击下方公众号卡片,直接关注。关注送《100个go常见的错误》pdf文档。

分类:

后端

标签:

后端

作者介绍

Go学堂
V1