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

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

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

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

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

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

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

now := time.Now()
fmt.Println(now) // 2025-06-29 12:29:16.112605 +0800 CST m=+0.000154626

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

now := time.Now()

year, month, day := now.Date()
hour, minute, second := now.Clock() fmt.Println(year, month, day) // 2025 June 29
fmt.Println(hour, minute, second) // 15 29 50

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

type Month int

const (
January Month = 1 + iota
February
March
April
May
June
July
August
September
October
November
December
)

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

fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)

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

now := time.Now()

fmt.Println(now.Year())
fmt.Println(now.Month())
fmt.Println(now.Day())
fmt.Println(now.Hour())
fmt.Println(now.Minute())
fmt.Println(now.Second())

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

1. 时间格式转字符串

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

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

now := time.Now()

fmt.Println(now.Format("2006-01-02 15:04:05")) // 2025-06-29 22:45:11

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

now := time.Now()

fmt.Println(now.Format("2006/01/02"))  // 2025/06/29
fmt.Println(now.Format("15:04:05")) // 23:22:06

2. 字符串转时间格式

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

timeStr := "2025/06/29"

t, err := time.Parse("2006/01/02", timeStr)
if err != nil {
fmt.Println("str to time error: ", err)
} else {
fmt.Println("str to time is: ", t)
} // str to time is: 2025-06-29 00:00:00 +0000 UTC

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

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

timeStr = "20:24:24"

t, err := time.Parse("15:04:05", timeStr)
if err != nil {
fmt.Println("str to time error: ", err)
} else {
fmt.Println("str to time is: ", t)
} // str to time is: 0000-01-01 20:24:24 +0000 UTC

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

timeStr := "2025/13/29"

t, err := time.Parse("2006/01/02", timeStr)
if err != nil {
fmt.Println("str to time error: ", err)
} else {
fmt.Println("str to time is: ", t)
} // str to time error: parsing time "2025/13/29": month out of range

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

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

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

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

now := time.Now()

fmt.Println(now.Unix())  // 1751211429

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

now := time.Now()

fmt.Println(now.UnixMilli())  // 1751211522339
fmt.Println(now.UnixNano()) // 1751211522339955000

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

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

timestamp := 1751211429
targetTime := time.Unix(int64(timestamp), 0)
fmt.Println(targetTime) // 2025-06-29 23:37:09 +0800 CST

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

4、日期与时间的加减

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

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

time.Duration

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

const (
minDuration Duration = -1 << 63
maxDuration Duration = 1<<63 - 1
)

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

const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)

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

时分秒的加减
now := time.Now()

threeHoursLater := now.Add(3 * time.Hour)
thirtyMinutesAgo := now.Add(-30 * time.Minute)
twoDaysLater := now.Add(48 * time.Hour) fmt.Println("now is: ", now.Format("2006-01-02 15:04:05"))
fmt.Println("three hours later is: ", threeHoursLater.Format("2006-01-02 15:04:05"))
fmt.Println("thirty minutes ago is: ", thirtyMinutesAgo.Format("2006-01-02 15:04:05"))
fmt.Println("two days later is: ", twoDaysLater.Format("2006-01-02 15:04:05"))

输出结果如下:

now is:  2025-06-30 22:58:38
three hours later is: 2025-07-01 01:58:38
thirty minutes ago is: 2025-06-30 22:28:38
two days later is: 2025-07-02 22:58:38

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

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

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

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

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

now := time.Now()

lastMonth := now.AddDate(0, -1, 0)
latestThreeDays := now.AddDate(0, 0, 3) fmt.Println("last month is: ", lastMonth.Format("2006-01-02 15:04:05"))
fmt.Println("latest three days is: ", latestThreeDays.Format("2006-01-02 15:04:05"))

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

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

使用 Add() 函数:

now := time.Now()

for i := range 7 {
targetDate := now.Add(time.Duration(24*(i+1)) * time.Hour)
fmt.Println(targetDate.Format("2006-01-02"))
}

使用 AddDate() 函数:

now := time.Now()

for i := range 7 {
targetDate := now.AddDate(0, 0, i+1)
fmt.Println(targetDate.Format("2006-01-02"))
}

4. 两个时间点的差值

1) Sub()

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

t1 := time.Now()
time.Sleep(3 * time.Second)
t2 := time.Now() subResult := t2.Sub(t1)
fmt.Println(subResult) // 3.00144925s

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

fmt.Println("use millseconds: ", subResult.Milliseconds())  // use millseconds:  3001
fmt.Println("use minutes: ", subResult.Minutes()) // use minutes: 0.05002786111666667
2) time.Since()

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

t1 := time.Now()
time.Sleep(2 * time.Second) result := time.Since(t1)
fmt.Println(result) // 2.001462166s

5、星期数的获取

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

now := time.Now()

fmt.Println(now.Weekday())  // Monday

输出的信息是 Monday

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

type Weekday int

const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)

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

now := time.Now()

fmt.Printf("%s, %d\n", now.Weekday(), now.Weekday())  // Monday, 1

6、定时器与计时器

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

1. 定时器

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

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

1) time.NewTimer

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

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

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

func RandomTimeWork() int {
sleepSeconds := rand.Intn(10)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
return sleepSeconds
}

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

func CallFunc(ch chan int) {
result := RandomTimeWork()
ch <- result
}

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

timeout := time.NewTimer(5 * time.Second)

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

ch := make(chan int)
go CallFunc(ch)

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

select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
case <-timeout.C:
fmt.Println("call func timeout")
}

其整体代码如下:

package main

import (
"fmt"
"math/rand"
"time"
) func RandomTimeWork() int {
sleepSeconds := rand.Intn(10)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
return sleepSeconds
} func CallFunc(ch chan int) {
result := RandomTimeWork()
ch <- result
} func main() {
timeout := time.NewTimer(5 * time.Second) ch := make(chan int)
go CallFunc(ch) select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
case <-timeout.C:
fmt.Println("call func timeout")
}
}

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

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

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

select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
timeout.Stop()
case <-timeout.C:
fmt.Println("call func timeout")
}
2) time.After

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

func main() {
ch := make(chan int)
go CallFunc(ch) select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
case <-time.After(5 * time.Second):
fmt.Println("call func timeout")
}
}

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

2. 计时器

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

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

func TargetFunc() {
fmt.Println("call target func at: ", time.Now().Format("2006-01-02 15:04:05"))
} func CallFuncEntrance(ticker *time.Ticker) {
// for t := range ticker.C {
for range ticker.C {
TargetFunc()
}
} func main() {
ticker := time.NewTicker(3 * time.Second) go CallFuncEntrance(ticker) time.Sleep(10 * time.Second)
ticker.Stop()
}

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

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

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

func main() {
ticker := time.NewTicker(3 * time.Second) go CallFuncEntrance(ticker) time.Sleep(10 * time.Second)
ticker.Reset(1 * time.Second)
time.Sleep(4 * time.Second)
ticker.Stop()
}

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

定时任务的其他实现方式

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

func TargetFunc() {
fmt.Println("call target func at: ", time.Now().Format("2006-01-02 15:04:05"))
} func CallFuncEntrance() {
for {
TargetFunc()
time.Sleep(3 * time.Second)
}
} func main() {
go CallFuncEntrance()
time.Sleep(10 * time.Second)
}

Golang基础笔记十一之日期与时间处理的更多相关文章

  1. MYSQL学习笔记三:日期和时间函数

    MYSQL学习笔记三:日期和时间函数 1. 获取当前日期的函数和获取当前时间的函数 /*获取当前日期的函数和获取当前时间的函数.将日期以'YYYY-MM-DD'或者'YYYYMMDD'格式返回 */ ...

  2. Java基础篇(04):日期与时间API用法详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.时间和日期 在系统开发中,日期与时间作为重要的业务因素,起到十分关键的作用,例如同一个时间节点下的数据生成,基于时间范围的各种数据统计和分 ...

  3. python笔记7:日期和时间

    Python 提供了一个 time 和 calendar 模块可以用于格式化日期和时间. 时间间隔是以秒为单位的浮点小数. 每个时间戳都以自从1970年1月1日午夜(历元)经过了多长时间来表示. 时间 ...

  4. 《Javascript权威指南》13号学习笔记:使用日期和时间

    一.创Date示例 1.Date类的方法和属性是非常不静,故,申请书Date属性和方法之前.必须创建Date类的实例. var date = new Date();  //以当前日期和时间创建实例. ...

  5. 吴裕雄--天生自然python学习笔记:Python3 日期和时间

    Python 程序能用很多方式处理日期和时间,转换日期格式是一个常见的功能. Python 提供了一个 time 和 calendar 模块可以用于格式化日期和时间. 时间间隔是以秒为单位的浮点小数. ...

  6. 基础笔记4(包装类,时间date. calendar

    1.包装类 基本类型和对象. 编译器会对基本类型和包装类进行自动拆箱,装箱处理 Interger  i=5; int i=new Interger(4); 一个缓存问题:以便提高效率 integer ...

  7. Golang基础笔记

    <基础> Go语言中的3个关键字用于标准的错误处理流程: defer,panic,recover. 定义一个名为f 的匿名函数: Go 不支持继承和重载. Go的goroutine概念:使 ...

  8. javascript中关于日期和时间的基础知识

    × 目录 [1]标准时间 [2]字符串 [3]闰年[4]月日[5]星期[6]时分秒 前面的话 在介绍Date对象之前,首先要先了解关于日期和时间的一些知识.比如,闰年.UTC等等.深入了解这些,有助于 ...

  9. R语言学习 第十一篇:日期和时间

    R语言的基础包中提供了三种基本类型用于处理日期和时间,Date用于处理日期,它不包括时间和时区信息:POSIXct/POSIXlt用于处理日期和时间,其中包括了日期.时间和时区信息.R内部在存储日期和 ...

  10. Java基础之一组有用的类——生成日期和时间(TryDateFormats)

    控制台程序. java.util包中含有相当多的类涉及日期和时间,包括Date类.Calendar类和GregorianCalendar类. Date类对象其实定义了精确到毫秒的时刻,从1970年1月 ...

随机推荐

  1. MySQL 中的 Log Buffer 是什么?它有什么作用?

    MySQL 中的 Log Buffer 是什么?它有什么作用? Log Buffer 是 MySQL InnoDB 存储引擎的一部分,用于存储写入日志数据的内存区域.它主要用于记录事务的变更日志,这些 ...

  2. .net6 Api添加跨域

    参照:(7条消息) .net6使用最小api(8)- 开启跨域模式,通过扩展服务实现_hailang2ll的博客-CSDN博客 步骤: 一.在appsetting.json里添加配置文件 //配置文件 ...

  3. pystinger实现不出网情况下,上线CS的方式

    某hw过程中遇到如下情况: 获取到webshell,目标服务器不出网 目标机:内网地址,端口映射到公网ipvps: pystinger地址: https://github.com/FunnyWolf/ ...

  4. Allure2+Maven+Testng部署及使用详细教程

    AllureReport部署 前言:最近做自动化测试要用到AllureReport生成测试报告,网上逛了一下,发现有很多帖子,但是大家描述的都模棱两可,没有详细的步骤,因此编写此贴对部署方式进行记录; ...

  5. Tableau 我常用函数整理

    日期函数 dateadd datedadd (date_part, interval, date) 表示在日期 date 的基础上, 以date_part 为单位, 与之间隔 interval的日期 ...

  6. 网络编程:epoll

    原理 select 的几个缺点: 1)每次调用select,都需要把fd集合从用户空间拷贝到内核空间,这个开销在fd很多时会很大 2)每次调用select都需要在内核遍历传递进来的所有fd,这个开销在 ...

  7. React-Native开发鸿蒙NEXT-video

    React-Native开发鸿蒙NEXT-video 前几周的开发,基本把一个"只读型"社区开发的差不多了.帖子列表,详情,搜索都迁移实现了,但还差了一点------视频类型帖子的 ...

  8. c++复习板子

    数论 GCD 点击查看 B4025 最大公约数 gcd: 点击查看代码 #include<bits/stdc++.h> #define int long long using namesp ...

  9. BS直聘职位数据采集与分析(爬虫)

    一.项目介绍 在当今竞争激烈的就业市场中,及时掌握职位信息和市场动态变得尤为重要.本文将详细介绍如何使用Python开发一个爬虫项目,自动采集BOSS直聘网站的职位数据,并对数据进行处理和分析. 1. ...

  10. 把多个文件打包压缩成tar.gz文件并解压的Java实现

    压缩文件   在Java中,可以 使用GZIPOutputStream创建gzip(gz)压缩文件,它在commons-compress下面,可以通过如下的maven坐标引入: <depende ...