time

Go内置的time包提供了时间显示和测量的函数

  • 时间可分为时间点和时间段,为此Go提供了两种基础数据类型time.Timetime.Duration
  • 所有与时间相关的业务都是基于时间点而衍生的,两个时间点组成一个时间段。
  • 时间包括时间值和时区,没有包含时区信息的时间是不完整的、有歧义的。
  • 日历的计算采用的是公历
  • Go提供针对某些特定业务提供了诸如go.Location时区、go.Timer定时器...
功能 描述
time.Time 时间点
time.Duration 时间段
time.Location 时区
time.Timer 定时器
time.Ticker 周期触发定时的计时器
time.C 存放时间点的管道

时间类型

time包提供了一个数据类型time.Time用来表示时间,时间一般包含时间值和时区两部分,时间值是一个纳秒精度的时间点。

package time

type Time struct {
  wall uint64
  ext int64
  loc *Location
}

time.Time结构包含三个字段:

字段 类型 描述
wall uint64 墙上时钟,表示距离公元1年1月1日00:00:00UTC的秒数。
ext int64 时间模式,表示纳秒。
loc *time.Location 时区,处理偏移量。不同时区对应的时间也不同。

最准确的时间计算是使用“原子振荡周期”所计算的物理时钟(Atomic Clock,原子钟),又被定义为标准时间(International Atomic Time)。常用的UTC(Universal Time Coordinated,世界协调时间)就是利用原子钟为基准定义出来的,UTC标准时间以GMT(Greenwich Mean Time,格林威治时间)这个时区为基准。

本地时间 = UTC + 时区差
北京时间 = UTF + 8个小时

操作系统中存在两个不同的时钟,分别是单调时钟(monotonic time)、墙上时钟(wall time/real time)

  • 单调时钟是本机硬件的节拍数,不同机器没有可比性。由于石英钟本身的误差,时间会有闰秒等原因,因此单调时钟与墙上时钟是不一致的。
  • 墙上时钟又称为时钟时间,是进程运行的时钟总量,其值与系统中同时运行的进程数量有关。进程从开始运行到结束,时钟走过的时间,包括进程在阻塞和等待状态的时间。
墙上时钟 大小
flag 1位
时间戳 33位
纳秒 30位

时间模式分为:是否包含单调时钟、外部输入的时间

  • 若时间模式ext包含单调时钟,则wall字段中会存在一个时间戳秒代表墙上时钟和一个时间戳纳秒代表墙上时钟。
  • 若时间模式ext不包含单调时钟,则wall字段中会包含一个时间戳纳秒代表墙上时钟,ext字段则包含一个时间戳秒代表墙上时钟。

当前时间

  • time.Now()会返回当前本地时间对象
func Now() Time

例如:获取当前本地时间信息

t := time.Now()
fmt.Println(t) // 2021-12-28 13:10:36.5987998 +0800 CST m=+0.003958401

从时间对象中获取时间分量信息

时间分量 描述
t.Year()
t.Month()
t.Day()
t.Hour() 小时数
t.Minute() 分钟
t.Second() 秒数
t.Nanosecond() 纳秒,1毫秒 = 1000纳秒, 1秒 = 1000毫秒
t.Weekday() 星期几
t.YearDay() 一年中的第n天
t.Zone() 获取时区
t := time.Now()
fmt.Println(t)                                  // 2021-12-28 13:43:30.9266336 +0800 CST m=+0.004069101
fmt.Printf("Year = %d\n", t.Year())             // 2021
fmt.Printf("Month = %d\n", t.Month())           // 12
fmt.Printf("Day = %d\n", t.Day())               // 28
fmt.Printf("Hour = %d\n", t.Hour())             // 13
fmt.Printf("Minute = %d\n", t.Minute())         // 43
fmt.Printf("Second = %d\n", t.Second())         // 30
fmt.Printf("NanoSecond = %d\n", t.Nanosecond()) // 926633600
fmt.Printf("Weekday = %d\n", t.Weekday())       // 2
fmt.Printf("YearDay = %d\n", t.YearDay())       // 362
fmt.Println(t.Zone())                           // CST 28800

时间解析

time.Parse()会将字符串类型的日期时间转换为UTC标准的Time类型

package time

func Parse(layout, value string) (Time, error)

例如:将字符串"2021-01-04 12:00:00"转换为标准UTC的time.Time类型

t, err := time.Parse("2006-01-02 15:04:05", "2021-01-04 12:00:00")
if err != nil {
    panic(err)
}
fmt.Printf("%T\n", t)  // time.Time
fmt.Printf("%+v\n", t) // 2021-01-04 12:00:00 +0000 UTC

时间戳

UNIX时间戳(UNIX Timestamp)

  • Unix时间又称为POSIX时间,是UNIX或类UNIX操作系统使用的时间表示方式。
  • Unix时间表示从UTC的1970年1月1日0时0分0秒开始到现在的总秒数,不考虑闰秒。
func (t Time) Unix() int64
func (t Time) UnixNano() int64
时间戳 单位 长度
t.Unix() 10位
t.UnixNano() 纳秒 19位
s := t.Unix()
ns := t.UnixNano()
ms := t.UnixNano() / 1e6
fmt.Printf("timestamp = %d, unixnano = %d, ms = %d\n", s, ns, ms)
timestamp = 1618738171, unixnano = 1618738171513428200, ms = 1618738171513

19位纳秒转13位毫秒

ms := t.UnixNano() / 1e6
fmt.Println(ms) // 1618737494948
  • time.Unix()函数可将时间戳转换为时间点对象
func time.Unix(sec int64, nsec int64) time.Time

时间格式

time.Format()用于获取时间并格式化为字符串,格式化占位符2006-01-02 15:04:05表示GoLang诞生时间,此处为固定写法。

func (t Time) Format(layout string) string

例如:将时间点对象转换为指定格式的字符串

t := time.Now()
fmt.Println(t)//2021-04-18 17:53:23.8149167 +0800 CST m=+0.002995001

str := t.Format("2006-01-02 15:04:05")
fmt.Println(str)//2021-04-18 17:53:23

例如:将时间戳转换为指定格式的字符串

ts := time.Now().Unix()
fmt.Println(ts)//1618739719

str := time.Unix(ts,0).Format("2006-01-02 15:04:05")
fmt.Println(str)//2021-04-18 17:55:19

标准时间

  • UTC(Coordinated Universal Time,协调世界时)是最主要的世界时间标准,它以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间(Greenwich Mean Time,GMT)。
  • UTC是世界上调节时钟和时间的主要时间标准,它与0度经线的平太阳时相差不超过1秒,且不遵守夏令时。
  • UTC是最接近格林威治标准时间(GMT)的替代时间系统之一,对于大多数用途来说,UTC时间被认为与GMT时间互换,但GMT时间已不再被科学界所确定。
func (t Time) UTC() Time 

例如:将GMT格林威治时间转换为UTC时间

t := time.Now()
fmt.Println(t)//2021-04-18 17:57:51.7228428 +0800 CST m=+0.002020201

utc := t.UTC()
fmt.Println(utc)//2021-04-18 09:57:51.7228428 +0000 UTC

时区

时间其实包括时间值和时区,没有时区信息的时间是不完整的。使用没有时区的非标准时间表示格式是有隐患的,由于解析时会根据使用场景的默认设置,如系统时区、数据库默认时区等,可能会引发事故。因此需确保服务器系统、数据库、应用程序时间统一的时区。

time包存在两个时区变量分别是time.UTC即UTC时间、time.Local本地时间

time.Location()函数返回时间点对象的地点和时区信息

func (t Time) Location() *Location

例如:获取时间点对应的时区

t := time.Now()
loc := t.Location()
fmt.Println(loc)//Local

time.LoadLocation()用于获取给定名字创建的时区

func time.LoadLocation(name string) (*time.Location, error)

例如:获取服务器所在时区

loc,err := time.LoadLocation("Asia/Shanghai")
if err!=nil {
    println(err)
}
println(loc)//0xc0000c2000

是否能够拿到时区取决于机器本地的zoneinfo文件,time.LoadLocation()函数将会返回一个*time.Location指针对象。

计时器

time.Ticker

time.Ticker是一个周期触发定时的计时器,一旦被定义每隔一段时间会自动触发。

type Ticker struct {
    C <-chan Time // 周期性传递时间信息的通道
    r runtimeTimer
}

time.Ticker会按照一个指定的时间间隔重复的向通道C发送系统当前时间值,通道的接收者可以按固定的时间间隔从通道中读取事件。

//实现原理
go func(t *time.Ticker){
    for{
        select{
        case <-t.C:
            fmt.Printf("ticker: run\n")
        }
    }
}(ticker)

goroutine周期性执行一些事务时非常有用,比如状态状态日志、输出、计算等。

例如:

ticker := time.NewTicker(time.Second * 3)
defer ticker.Stop()

fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
time.Sleep(time.Second * 4)

for{
    select{
    case <-ticker.C:
        fmt.Println(time.Now())
    }
}

time.NewTicker

NewTicker方法用于返回一个新的Ticker,Ticker中包含一个通道字段C会每隔时间段d就向该通道发送当前的时间。

func time.NewTicker(d time.Duration) *time.Ticker
  • 时间间隔d的单位为纳秒(ns, int64),在工厂函数time.NewTicker中以time.Duration类型的参数传入。
  • *Ticker指向定时器的指针

例如:定时执行,循环执行不退出。

//创建计时器,每隔1秒后定时器会向channel发送一个事件,包含当前时间。
ticker := time.NewTicker(time.Second * 1)

i:=0
for{
    //执行若干次后退出
    if i>5 {
        break
    }
    i++

    fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
    //从打点器中获取通道
    <-ticker.C
}

//清理计时器
ticker.Stop()

例如:

//创建计时器,每隔1秒后定时器会向channel发送一个事件,包含当前时间。
ticker := time.NewTicker(time.Second * 1)

i:=0
for{
    //执行若干次后退出
    if i>5 {
        //清理计时器
        ticker.Stop()
        //跳出循环
        break
    }
    //从打点器中获取通道
    select{
    case <-ticker.C:
        i++
        fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
    }
}

例如:

ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for{
    select{
    case t := <-ticker.C:
        fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
    }
}

time.Tick

time.Tick()返回的是一个通道,每隔指定时间就会有数据从通道中出来。

 func Tick(d Duration) <-chan Time {
    if d <= 0 {
        return nil
    }
    return NewTicker(d).C
}

实现固定时间重复执行,即定期循环。

for{
    time.Sleep(time.Second * 2)
    fmt.Println(time.Now())
}

更优雅的方案

  • range是可以不接收遍历的值的,若不接收则可简写。
for range time.Tick(time.Second * 2){
    fmt.Println(time.Now())
}

ticker.Stop

func (t *Ticker) Stop() {
    stopTimer(&t.r)
}
  • ticker.Stop()方法会关闭一个Ticker,关闭后将不会再发送更多的tick信息。
  • ticker.Stop()方法不会关闭通道t.C,只能避免从通道的读取不正确的成功。

ticker.Reset

func (t *Ticker) Reset(d Duration) {
    if t.r.f == nil {
        panic("time: Reset called on uninitialized Ticker")
    }
    modTimer(&t.r, when(d), int64(d), t.r.f, t.r.arg, t.r.seq)
}

时间段

time.Duration

  • time.Durationtime包定义的一个类型,表示一段时间间隔。
  • Duration类型表示两个时间点之间经过的时间段,以纳秒为单位,可表示的最长时间段大约290年。
  • Duration时间段实际上是int64类型,单位纳秒。

实际编程中不会直接赋值时间段为一个数字,而是会采用time包提供的常量。

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

时间运算

Add

  • 获取时间点增加时间段后的时间点
func (t Time) Add(d Duration) Time

例如:计算1天后的时间点

t1 := time.Now()
fmt.Println(t1)//2021-04-18 18:27:30.482194 +0800 CST m=+0.002020201

t2 := t1.Add(time.Hour * 24)
fmt.Println(t2)//2021-04-19 18:27:30.482194 +0800 CST m=+86400.002020201

例如:计算1天前的时间点

t2 := t1.Add(-time.Hour * 24)
fmt.Println(t2)//2021-04-17 18:30:48.1375322 +0800 CST m=-86399.998035299

Sub

  • 计算两个事件之间的差值
func (t Time) Sub(u Time) Duration

例如:

t1 := time.Now()
fmt.Println(t1)//2021-04-18 18:27:30.482194 +0800 CST m=+0.002020201

t2 := t1.Add(time.Hour * 24)
fmt.Println(t2)//2021-04-17 18:30:48.1375322 +0800 CST m=-86399.998035299

d := t2.Sub(t1)
fmt.Println(d)//24h0m0s

时间比较

Equal

func (t Time) Equal(u Time) bool
  • 判断两个时间点tu是否相同,会考虑时区的影响。

例如:

t1 := time.Now()
fmt.Println(t1)//2021-04-18 18:27:30.482194 +0800 CST m=+0.002020201

t2 := t1.Add(time.Hour * 24)
fmt.Println(t2)//2021-04-17 18:30:48.1375322 +0800 CST m=-86399.998035299

flag := t2.Equal(t1)
fmt.Println(flag)//false

Before

func (t Time) Before(u Time) bool
  • 判断时间点t是否在指定时间点u之前

After

func (t Time) After(u Time) bool
  • 判断某时间点t是否在指定时间点u之后
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • 类型 time.Time{} 时间类型,包含了秒和纳秒以及Location time.Month() type M...
    吃猫的鱼0阅读 31,767评论 0 4
  • golang使用的版本: go version go1.10.3 一:功能介绍 time的一些功能,比如时区,像l...
    九卷技术录阅读 538评论 0 1
  • Go time Go中提供了很多关于时间处理的内容,这篇文章来主讲讲解下时间上的内容。 最外层可以直接使用的方法 ...
    qingshuiting阅读 929评论 0 1
  • time 包的官方文档 中文英文本文章不是对time包文档的重复,旨在加深对time实现的理解上,以上是文档可以进...
    _AlphaBaby_阅读 681评论 0 0
  • 原文链接 http://ironxu.com/747 介绍Go 标准库 time 常用导出函数,结构体及其方法。 ...
    好刚编程阅读 16,962评论 2 11