Golang语言标准库time之日期和时间相关函数

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 这篇文章是关于Go语言日期和时间处理的文章,介绍了如何使用Go标准库中的time包来处理日期和时间。

                                              作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.时间类型

package main

import (
    "time"
    "fmt"
)

func main() {
    // 获取当前时间对象,后从时间对象中可以获取到年、月、日、时、分、秒等信息。
    now := time.Now()
    fmt.Printf("current time: [%v]\n", now)

    // 年
    year := now.Year()
    // 月
    month := now.Month()
    // 日
    day := now.Day()
    // 小时
    hour := now.Hour()
    // 分钟
    minute := now.Minute()
    // 秒
    second := now.Second()
    fmt.Println(year, month, day, hour, minute, second)
}

二.Location和time zone

1.时区介绍

Go语言中使用location来映射具体的时区。时区(Time Zone)是根据世界各国家与地区不同的经度而划分的时间定义,全球共分为24个时区。

中国差不多跨5个时区,但为了使用方便只用东八时区的标准时即北京时间为准。在日常编码过程中使用时间对象的时候一定要注意其时区信息。

2.时区案例

package main

import (
    "fmt"
    "time"
)

func main() {
    // 中国没有夏令时,使用一个固定的8小时的UTC时差(东八区,UTC +08:00),对于很多其他国家需要考虑夏令时。
    timezone := int((8 * time.Hour).Seconds())

    // FixedZone 返回始终使用给定区域名称和偏移量(UTC 以东秒)的Location。UTC +08:00
    shanghaiTimezone := time.FixedZone("Asia/Shanghai", timezone)

    // 如果当前系统有时区数据库,则可以加载一个位置得到对应的时区,例如,加载纽约所在的时区,UTC -05:00
    newYorkTimezone, _ := time.LoadLocation("America/New_York")

    utc := time.Date(2009, 1, 1, 12, 0, 0, 0, time.UTC)
    shanghai := time.Date(2009, 1, 1, 20, 0, 0, 0, shanghaiTimezone)
    NewYork := time.Date(2009, 1, 1, 7, 0, 0, 0, newYorkTimezone)

    // 北京时间(东八区)比UTC早8小时,所以上面两个时间看似差了8小时,但表示的是同一个时间
    t1 := utc.Equal(shanghai)

    // 纽约(西五区)比UTC晚5小时,所以上面两个时间看似差了5小时,但表示的是同一个时间
    t2  := utc.Equal(NewYork)


    fmt.Printf("[%v] = [%v] => [%t]\n",utc,shanghai,t1)
    fmt.Printf("[%v] = [%v] => [%t]\n",utc,NewYork,t2)
}

三.时间戳unix time

1.Unix Time概述

Unix Time是自1970年1月1日 00:00:00 UTC至当前时间经过的总秒数。下面的代码片段演示了如何基于时间对象获取到Unix 时间。

2.获取时间戳

package main

import (
    "fmt"
    "time"
)

func main() {
    // 获取当前时间
    now := time.Now()

    // 秒级时间戳
    timestamp := now.Unix()

    // 毫秒时间戳 Go1.17+
    milli := now.UnixMilli()

    // 微秒时间戳 Go1.17+
    micro := now.UnixMicro()

    // 纳秒时间戳
    nano := now.UnixNano()

    fmt.Printf("秒级时间戳: %v\n毫级时间戳: %v\n微级时间戳: %v\n纳级时间戳: %v\n", timestamp, milli, micro, nano)
}

3.将时间戳转为时间对象

package main

import (
    "fmt"
    "time"
)

func main() {
    // 获取北京时间所在的东八区时区对象
    secondsEastOfUTC := int((8 * time.Hour).Seconds())
    beijing := time.FixedZone("Beijing Time", secondsEastOfUTC)

    // 北京时间 2022-02-22 22:22:22.000000022 +0800 CST
    t := time.Date(2030, 11, 11, 23, 59, 59, 59, beijing)

    var (
        sec  = t.Unix()
        msec = t.UnixMilli()
        usec = t.UnixMicro()
    )

    // 将秒级时间戳转为时间对象(第二个参数为不足1秒的纳秒数)
    t1 := time.Unix(sec, 99999)

    // 毫秒级时间戳转为时间对象
    t2 := time.UnixMilli(msec)

    // 微秒级时间戳转为时间对象
    t3 := time.UnixMicro(usec)

    fmt.Printf("t1 = [%v], t2 = [%v], t3 = [%v]\n", t1, t2, t3)
}

四.时间间隔设置及比较

1.时间间隔类型的常量

time.Duration是time包定义的一个类型,它代表两个时间点之间经过的时间,以纳秒为单位。

time.Duration表示一段时间间隔,可表示的最长时间段大约290年。

time包中定义的时间间隔类型的常量如下:

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


例如:time.Duration表示1纳秒,time.Second表示1秒。

2.Add

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    /*
        Go语言的时间对象有提供Add方法如下:
            func (t Time) Add(d Duration) Time
    */

    later := now.Add(time.Hour) // 当前时间加1小时后的时间

    fmt.Printf("now = [%v] later = [%v]\n", now, later)
}

3.sub

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()


    t1, _ := time.Parse("2006/01/02 15:04:05", "2024/08/20 11:25:20")

    /*
        Go语言的时间对象有提供Sub方法如下:
            func (t Time) Sub(u Time) Duration
    */
    t2 := now.Sub(t1) // 比较现在和t1时间相差的时间

    fmt.Printf("now = [%v] t2 = [%v]\n", now, t2)
}

4.Equal

package main

import (
    "fmt"
    "time"
)

func main() {

    t1, _ := time.Parse("2006/01/02 15:04:05", "2030/08/20 11:25:20")

    t2, _ := time.Parse(time.RFC3339, "2030-08-20T19:25:20+08:00")

    /*
        Go语言的时间对象有提供Equal方法如下:
            func (t Time) Equal(u Time) bool

        判断两个时间是否相同,会考虑时区的影响,因此不同时区标准的时间也可以正确比较。

        本方法和用t==u不同,这种方法还会比较地点和时区信息。
    */
    flag := t1.Equal(t2)

    fmt.Printf("[%v] = [%v] ---> [%t]\n", t1, t2, flag)
}

5.Before

package main

import (
    "fmt"
    "time"
)

func main() {

    t1, _ := time.Parse("2006/01/02 15:04:05", "2030/08/21 12:25:20")

    t2, _ := time.Parse(time.RFC3339, "2030-08-30T19:25:20+08:00")

    /*
        Go语言的时间对象有提供Before方法如下:
                func (t Time) Before(u Time) bool

        如果t代表的时间点在u之前,返回真;否则返回假。
    */
    flag := t1.Before(t2)

    fmt.Printf("[%v] = [%v] ---> [%t]\n", t1, t2, flag)
}

6.After

package main

import (
    "fmt"
    "time"
)

func main() {

    t1, _ := time.Parse("2006/01/02 15:04:05", "2030/09/21 12:25:20")

    t2, _ := time.Parse(time.RFC3339, "2030-08-30T19:25:20+08:00")

    /*
        Go语言的时间对象有提供Before方法如下:
            func (t Time) After(u Time) bool

        如果t代表的时间点在u之后,返回真;否则返回假。
    */
    flag := t1.After(t2)

    fmt.Printf("[%v] = [%v] ---> [%t]\n", t1, t2, flag)
}

五.定时器

1.定时器概述

使用time.Tick(时间间隔)来设置定时器,定时器的本质上是一个通道(channel)。

2.定时器案例

package main

import (
    "fmt"
    "time"
)

func main() {
    // //定义一个1秒间隔的定时器,如果想要换成每分钟,可以使用"time.Minute",依此类推...
    ticker := time.Tick(time.Second)

    for i := range ticker {
        //每秒都会执行的任务
        fmt.Println(i)
    }
}

六.时间格式化

1.时间格式化

time.Format函数能够将一个时间对象格式化输出为指定布局的文本表示形式,需要注意的是Go语言中时间格式化的布局不是常见的Y-m-d H:M:S,而是使用"2006-01-02 15:04:05.000"(记忆口诀为2006 1 2 3 4 5)。

其中:
    - 2006:年(Y)
    - 01:月(m)
    - 02:日(d)
    - 15:时(H)
    - 04:分(M)
    - 05:秒(S)


温馨提示:
    (1)如果想格式化为12小时格式,需在格式化布局中添加PM。
    (2)小数部分想保留指定位数就写0,如果想省略末尾可能的0就写9。

2.参考案例

package main

import (
    "fmt"
    "time"
)

func main() {
    // 获取当前时间对象,后续方便基于对时间对象进行格式化操作
    now := time.Now()

    // 格式化的模板为 2006-01-02 15:04:05

    // 24小时制
    fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan"))

    // 12小时制
    fmt.Println(now.Format("2006-01-02 03:04:05.000 PM Mon Jan"))

    // 小数点后写0,因为有3个0所以格式化输出的结果也保留3位小数
    fmt.Println(now.Format("2006/01/02 15:04:05.000"))

    // 小数点后写9,会省略末尾可能出现的0
    fmt.Println(now.Format("2006/01/02 15:04:05.999"))

    // 只格式化时分秒部分
    fmt.Println(now.Format("15:04:05"))

    // 只格式化日期部分
    fmt.Println(now.Format("2006.01.02"))
}

七.解析字符串的时间

1.解析时间的函数

对于从文本的时间表示中解析出时间对象,time包中提供了time.Parse和time.ParseInLocation两个函数。

其中time.Parse在解析时不需要额外指定时区信息。

time.ParseInLocation函数需要在解析时额外指定时区信息。

2.Parse从文本解析时间

package main

import (
    "fmt"
    "time"
)

func main() {
    // 在没有时区指示符的情况下,time.Parse 返回UTC时间
    t1, _ := time.Parse("2006/01/02 15:04:05", "2030/10/05 11:25:20")

    // 在有时区指示符的情况下,time.Parse 返回对应时区的时间表示
    // RFC3339     = "2006-01-02T15:04:05Z07:00"
    t2, _ := time.Parse(time.RFC3339, "2030-10-05T11:25:20+08:00")

    fmt.Printf("t1 = [%v], t2 = [%v]\n", t1, t2)
}

3.ParseInLocation从文本解析时间

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()

    // 加载时区
    timezone, _ := time.LoadLocation("Asia/Shanghai")

    // 按照指定时区和指定格式解析字符串时间
    t1, _ := time.ParseInLocation("2006/01/02 15:04:05", "2030/07/20 11:25:20", timezone)

    fmt.Println(now)

    fmt.Println(t1.Sub(now))
}

八.练习题

1.获取当前时间,格式化输出为"2030/01/01 23:30:05"格式

package main

import (
    "fmt"
    "time"
)

func PrintTime(t time.Time) {
    // 方式一: Go语言中时间格式化的布局不是常见的Y-m-d H:M:S,而是使用 2006-01-02 15:04:05.000(记忆口诀为2006 1 2 3 4 5)
    // 温馨提示: 
    //         1."2006/01/02 15:04:05"分别对应着年月日时分秒;
    //        2.如果只想看年则传入"2006",如果想要看月份则传入"01",如果想要看小时则传入15,以此推类;
    t1 := t.Format("2006/01/02 15:04:05")
    fmt.Printf("t1 = [%v]\n", t1)

    // 方式二: 使用Sprintf
    t2 := fmt.Sprintf("%d/%d/%d %d:%d:%d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
    fmt.Printf("t2 = [%v]\n", t2)

}

func main() {
    now := time.Now()
    PrintTime(now)
}

2.编写程序统计一段代码的执行耗时时间,单位精确到微秒。

package main

import (
    "fmt"
    "time"
)

func calctime1() {
    // 计算出纳秒
    start := time.Now().UnixNano() / 1000 

    fmt.Println("正在执行代码...")
    time.Sleep(time.Millisecond * 50)
    end := time.Now().UnixNano() / 1000

    fmt.Printf("in calctime1()... 耗费了%v微妙\n", end-start)

}

func calctime2() {
    start := time.Now()
    fmt.Println("正在执行代码...")
    time.Sleep(time.Millisecond * 100)

    // 注意, time.Since时内置的方法,就是拿当前的时间减去start的时间哟。
    fmt.Printf("in calctime2()... 耗费了%v\n", time.Since(start))

}

func main() {
    calctime1()
    calctime2()
}
目录
相关文章
|
1月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
53 4
Golang语言之管道channel快速入门篇
|
1月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
52 3
Golang语言之gRPC程序设计示例
|
1月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
43 4
|
4天前
|
算法 Go
Golang限流器time/rate正确打开姿势
本文详细探讨了 Go 语言限流工具 `golang.org/x/time/rate` 包下的 `Limiter` 类,并通过示例展示了如何使用该工具实现 QPS 限流功能。作者深入分析了 `Limiter` 的内部工作机制,揭示了其独特的算法设计,并指出了在动态调整限流参数时可能遇到的问题及解决方法。此外,还对比了该算法与传统令牌桶和滑动窗口算法的区别,总结了其优缺点。最后,作者给出了修正限流问题的具体代码示例。
Golang限流器time/rate正确打开姿势
|
1月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
27 4
Golang语言goroutine协程篇
|
1月前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
25 3
Golang语言之Prometheus的日志模块使用案例
|
1月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
46 4
Golang语言文件操作快速入门篇
|
1月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
39 3
|
1月前
|
Go
Golang语言之函数(func)进阶篇
这篇文章是关于Golang语言中函数高级用法的教程,涵盖了初始化函数、匿名函数、闭包函数、高阶函数、defer关键字以及系统函数的使用和案例。
20 3
Golang语言之函数(func)进阶篇
|
1月前
|
Go
Golang语言之函数(func)基础篇
这篇文章深入讲解了Golang语言中函数的定义和使用,包括函数的引入原因、使用细节、定义语法,并通过多个案例展示了如何定义不返回任何参数、返回一个或多个参数、返回值命名、可变参数的函数,同时探讨了函数默认值传递、指针传递、函数作为变量和参数、自定义数据类型以及返回值为切片类型的函数。
23 2
Golang语言之函数(func)基础篇