Golang 基础笔记十一之日期与时间处理 - XHunter

本文首发于公众号:Hunter 后端

原文链接:Golang 基础笔记十一之日期与时间处理

Golang基础笔记十一之日期与时间处理

本篇笔记介绍 Golang 里日期与时间的处理,以下是本篇笔记目录:

  1. 当前日期与时间的获取
  2. 字符串与时间格式的互相转换
  3. 时间戳与时间格式的互相转换
  4. 日期与时间的加减
  5. 星期数的获取
  6. 定时器与计时器

1、当前日期与时间的获取

在 Golang 里,日期和时间处理都通过 time 包来实现。

如果我们想获取当前时间,我们可以使用 time.Now() 来操作:

如果我们想获取单独的年月日时分秒字段,可以使用下面的操作:

可以看到输出的月份是 June,但实际上月份是一个自定义的类型 Month,本质上也是一个 int 型数据,其源代码如下:

所以,对于这里的年月日时分秒的单个变量,我们想将其组合输出为一般的%Y-%m-%d %H:%M:%S 格式,可以如下操作:

我们也可以分别单独获取对应的年月日时分秒数据:

2、字符串与时间格式的互相转换

1. 时间格式转字符串

在其他计算机语言中,如果想将时间字段转化为字符串,格式化的操作比如 Python,一般是类似于%Y-%m-%d %H:%M:%S 这种,但是 Golang 里是一个特殊的格式化字段,为 2006-01-02 15:04:05。

Go 里对时间字段格式化的函数为 Format(),下面是将时间格式转为字符串的操作为:

当然,格式化操作也可以单独针对日期,或者时间,连接的符号也可以自定义:

2. 字符串转时间格式

字符串转时间格式使用 time.Parse() 函数,以下是一个测试:

在这里 time.Parse() 返回两个字段,一个是转换后的时间字段,一个是转换过程中的错误。

上面是转换日期,转换时间也是一样的操作:

而如果提供了错误的时间字符串,返回的 err 字段则不会为空,比如下面这个示例:

3、时间戳与时间格式的互相转换

另一个在时间函数中常用到的用于转换的数据是时间戳,下面介绍一下时间戳与时间的互相转换。

1. 时间格式转换为时间戳

下面是时间格式转换为秒级的时间戳:

还有转换为毫秒,纳秒级的操作:

2. 时间戳转换为时间格式

时间戳转换为时间格式的函数为 time.Unix():

这里需要注意,输入的时间戳需要是 int64 类型,输入的第二个参数为纳秒值,如果不需要那么精细的话,传值为 0 即可。

4、日期与时间的加减

介绍日期与时间的加减,这里分为两部分来介绍,一部分是时分秒,一部分是年月日,他们分别用到的函数是 Add() 和 AddDate()。

1. Add()- 时分秒的加减

time.Duration

Add() 函数的参数类型是 time.Duration,Duration 也是自定义的一个时间单位,一纳秒就是一个 Duration,它的类型是 int64,范围是:

而时分秒也都根据其转换关系定义好了各自的字段:

所以我们在使用 Add() 函数的时候可以直接使用对应的单位。

时分秒的加减

输出结果如下:

2. AddDate()- 年月日的加减

AddDate() 函数接收三个参数,分别是 years、months、days,表示需要在当前时间需要增加的年数、月数和天数。

如果是想要回溯过去的日期,在对应的参数前加上负号 - 即可。

如果是不需要指定的参数设为 0 即可。

比如想要获取今天前一个月的日期,以及今天往后三天的日期,可以如下操作:

3. Add() 和 AddDate() 使用示例

这里分别使用 Add() 和 AddDate() 两个函数打印出之后七天的日期,其操作如下:

使用 Add() 函数:

使用 AddDate() 函数:

4. 两个时间点的差值

1) Sub()

如果我们想获取两个时间点之间差值,比如用于测试某个函数执行的时间,可以使用 Sub() 函数,返回的结果也是 time.Duration:

我们可以将 subResult 直接转换成我们想要的单位,比如毫秒和分钟:

2) time.Since()

如果我们想获取现在距离某个时间点的差值,也可以直接使用 time.Since() 函数,其使用示例如下:

5、星期数的获取

我们也可以根据日期来获取对应的星期数,比如今天是星期几:

输出的信息是 Monday。

星期数的底层数据也是 int 型,但是 Golang 将其包了一层,自定义了一个 Weekday 的类型:

从周日开始到周六,分别是从 0 到 6,我们可以打印一下:

6、定时器与计时器

下面介绍一下 Golang 里 time 模块的定时器和计时器如何使用。

1. 定时器

定时器有两个写法,一个是 time.NewTimer(),一个是 time.After(),接收的参数类型都是 time.Duration。

下面直接用代码示例来介绍如何使用。

1) time.NewTimer

有一个需求,我们需要调用某个函数,但是函数的执行时长是不定的,而整体执行的时长是有限的,我们希望能在指定的时间内返回数据,如果这个函数执行超时就不希望它再执行了,能够立即获取其超时状态。

针对这个需求,我们就可以使用定时器来完成。

首先,我们有一个需要执行的函数,这个函数可能是调用某个接口,可能是从 Redis 或者 MySQL 中读数据,但是其执行时长是不定的,我们用 RandomTimeWork() 函数来替代,并且在其中设置一个随机休息的时间用来模拟不定的执行时长:

然后我们需要一个中间函数使用通道来传递其返回值:

接下来就是主函数的操作,我们需要先设置一个定时器,这里我们设置为 5 秒的超时:

然后设置一个通道用于传输数据,并且使用 goroutine 来调用:

最后就是使用 select 操作来进行等待,看是通道先返回数据,还是定时器先计时完毕:

其整体代码如下:

在上面的操作中,如果待执行的函数先执行完毕,而定时器却没有结束,我们可以手动执行停止定时器操作。

实际上,如果不手动停止,默认等待定时器触发结束或者程序完毕也可以,但是在高并发场景下,如果有很多未完成的定时器会造成内存占用增加,且增加程序的 GC 负担,因此,我们可以选择手动提前停止定时器。

停止操作也很简单,在获取到函数执行的结果后,我们可以如下操作:

2) time.After

除了 time.NewTimer(),我们可以使用更简单的 time.After() 函数来执行一个定时器,相对上面的完整示例,我们只改动 main 函数里代码如下:

注意:使用 time.After() 有个问题就是不可以提前手动结束定时器。

2. 计时器

计时器常用于定时任务,比如每隔多长时间执行某个动作,用到的函数是 time.NewTicker,传入的参数是 time.Duration。

比如我们想将某个函数设置为每隔三秒钟执行一次,我们可以如下操作:

上面的示例中,开启了一个 goroutine 并将计时器作为参数传入,每隔三秒钟触发一次目标函数 TargetFunc()。

并且在最后执行了计时器的停止操作 ticker.Stop()。

在这里如果我们想重置计时器的间隔时间,可以使用 Reset() 操作:

执行 main 函数可以看到目标函数执行的时间间隔会从 3s 变成 1s。

定时任务的其他实现方式

除了这个操作用来执行定时任务外,我们还可以使用 for{}和 time.Sleep() 操作来实现定时任务,其示例如下: