『Go 内置库第一季:time』

This_is_Fine_Gopher.png

大家好,我叫谢伟,是一名程序员。

近期会更新内置库的学习笔记,主要参考文献来自于官方文档和源代码。

本节的主题:time

时间的操作在项目中使用的非常频繁,比如说数据库中,经常有时间的操作,比如根据时间进行划分,统计之类的功能。

那么如何学会常用的操作呢?

大纲:

  • 自己总结的常用操作
  • 官方的 API
  • 学习总结

自己总结的常用操作

时间类型的数据,我们在项目中都会进行哪些操作呢?

要在思维里掌握时间类型的数据的操作,又应该如何梳理呢?

1. 时间的单位

暂时撇开代码层面,日常生活中关于时间的单位都有哪些呢?

  • 毫秒

一般到毫秒层面就够了,当然还可以继续划分:微秒、纳秒...

那时间的单位的转换是如何进行的呢?

  • 1 y = 12 m
  • 1 m = 4 w
  • 1 w = 7 d
  • 1 d = 24 h
  • 1 h = 60 min
  • 1 min = 60 s
  • 1 s = 1000 ms
  • 1 ms = 1000 us
  • 1 us = 1000 ns

为什么要了解这些啊? 这不是常识吗?是的。经常有常识性的问题,转换成代码层面而出错。所以有必要进行了回顾。

2. 时间操作

了解了时间的基本单位,那就好办了。

func main(){
    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())
    fmt.Println(now.Date())

}
>>
2018-11-13 22:30:03.500763 +0800 CST m=+0.000810351
2018
November
13
22
30
3
2018 November 13

所有这些时间的单位底层都是 int 类型。既然是 int 类型,那 November 是如何得到的?

很简单,底层定义这样一个关于月份的数组,将对应的位置的值返回即可。


var months = [...]string{
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
}

3. 时间戳

  • 时间戳的使用也非常广泛,比如为了表示唯一性
  • 时间戳的概念:1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数

时间戳,这个概念,如果不是程序员,可能完成没有必要了解,有更好的阅读时间的显示方式,没必要知道这么一串数字。

在 Go 中,如何将时间转换成时间戳?

func main(){
    now := time.Now()
    
    fmt.Println(now.UnixNano())
    fmt.Println(now.Unix())
}
>>
1542119403501037000
1542119403

上文一个转换为纳秒,一个是转换为秒。可以看到转换单位是 10^9

那如何将时间戳转换为 时间类型?


func main(){

    fmt.Println(time.Unix(1542119403, 1542119403000000000))
    
    fmt.Println(time.Unix(1542119403, 1542119403000000000).UTC())

}

>>
2067-09-26 13:00:06 +0800 CST
2067-09-26 05:00:06 +0000 UTC

关于时间,还存在一个重要的概念,即:时区

  • UTC 校准的全球时间
  • CST China Standard Time UT 8:00

所以经常会出现时间相差 8 小时的情况,比如,本地程序运行正常,推送到仓库中,自动构建够,程序得不到预期的值,有可能是因为服务器的时间的时区和本地的不一致。

4. 时间和字符串的相互转化

涉及时间的显示的布局有个默认值:2006-01-02 15:04:05

这个时间的速记:123456

func main(){
    
    now := time.Now()
    fmt.Println(now.Format("2006-01-02 15:04:05"))
}

    timeString := "2019-01-02 15:03:00"

    fmt.Println(time.Parse("2006-01-02 15:04:05", timeString))
    fmt.Println(time.ParseInLocation("2006-01-02 15:04:05", timeString, time.Local))

>>
2018-11-13 22:59:08
2019-01-02 15:03:00 +0000 UTC <nil>
2019-01-02 15:03:00 +0800 CST <nil>

还记得之前字符串和基本数据类型(整型、浮点型、布尔型)相互转化的章节。

我们总结:

  • 基本数据类型转化为字符串:带关键字 Format, 且没有错误返回
  • 字符串转化为基本数据类型:带关键字 Parse , 且存在错误返回

时间类型和字符串之间的转化和上文的总结一致。

启发:使用关键字:Format 、 Parse 对自己函数进行命名组织;没有错误类型的函数不返回错误类型

有时候我们可能只需要时间的部分值,比如说,只需要整点的值,那关于这些操作,又应该如何操作呢?


func main(){
    timeToString()
}

var timeToString = func() {

    now := time.Now()

    fmt.Println(now.Format("2006-01-02 15:00:00"))
    fmt.Println(now.Format("2006-01-02 15:04:00"))
    fmt.Println(now.Format("2006-01-02 00:00:00"))

    fmt.Println(now.Round(time.Hour))
    fmt.Println(now.Round(time.Minute))
    fmt.Println(now.Round(time.Second))
    fmt.Println(now.Truncate(time.Hour))
    fmt.Println(now.Truncate(time.Minute))
    fmt.Println(now.Truncate(time.Second))

}
>>
2018-11-13 23:00:00
2018-11-13 23:12:00
2018-11-13 00:00:00
2018-11-13 23:00:00 +0800 CST
2018-11-13 23:13:00 +0800 CST
2018-11-13 23:12:34 +0800 CST
2018-11-13 23:00:00 +0800 CST
2018-11-13 23:12:00 +0800 CST
2018-11-13 23:12:33 +0800 CST

可以看出,存在两种方法,一种是操作 布局,即更改那个默认值 2006-01-02 15:04:05; 一种是使用内置的 Round, Truncate 两种的区别是Round 向上取整,Truncate 向下取整

5. 两个时间之间的操作

上面的例子绝大多数是单个时间的操作,比如取时间戳、时间和字符串之间转化、获取时间的年月日等。

日常操作中,两个时间的操作也是比较频繁的。

比如:

  • 判断一个时间是否是在该时间之前、之后
  • 给出给定时间的某个时间节点的值:比如 1小时前、1天前、1年前、1年后、1天后、1小时后等
func main(){
    timeOp()

}

var timeOp = func() {
    now := time.Now()

    fmt.Println(now.Add(1 * time.Hour))
    fmt.Println(now.Add(24 * time.Hour))
    fmt.Println(now.Add(-10 * time.Hour))
}

>>
2018-11-14 00:21:47.48055 +0800 CST m=+3600.000854277
2018-11-14 23:21:47.48055 +0800 CST m=+86400.000854277
2018-11-13 13:21:47.48055 +0800 CST m=-35999.999145723

得到 1小时后、1天后、10小时前

好,那这些时间前后都有哪些值可以选择?


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

这些常量,辅以恰当的运算。

那 1年前咋搞?还运算吗?傻瓜。


func main(){
    timeOpDate()
}

var timeOpDate = func() {
    now := time.Now()

    fmt.Println(now.AddDate(-1, 0, 0))
    fmt.Println(now.AddDate(2, 0, 0))
    fmt.Println(now.AddDate(0, 1, 0))
}
>>
2017-11-13 23:26:18.697797 +0800 CST
2020-11-13 23:26:18.697797 +0800 CST
2018-12-13 23:26:18.697797 +0800 CST

完成啦。

基本上这两个函数就可以完成目的。

那如何计算两个时间之间的差呢?


func main(){
    timeInterval()
}

var timeInterval = func() {
    now := time.Now()
    stringTime := "2018-11-14 20:00:00"

    newTime, _ := time.ParseInLocation("2006-01-02 15:04:05", stringTime, time.Local)

    if newTime.After(now) {
        subTime := newTime.Sub(now)
        fmt.Println("newTime after now")
        fmt.Println(subTime.Hours())
        fmt.Println(subTime.Minutes())
        fmt.Println(subTime.Seconds())
        fmt.Println(subTime.Nanoseconds())
    }
    if newTime.Before(now) {
        subTime := now.Sub(newTime)
        fmt.Println(subTime.String())
    }
}
>>
newTime after now
20.463746377777777
1227.8247826666666
73669.48696
73669486960000

6. 其他

  • 得出时间是本周的周几
  • 得出时间是该年的第几周
func main(){
    fmt.Println(time.Now().ISOWeek())
    fmt.Println(time.Now().Weekday())
}
>>
2018 46
Tuesday

官方API

大概我们没讲有这些内容:

  • 定时器?
  • 底层是如何获取时间的?
  • 序列化和反序列化(还记得 json 那节 Marshler 吗)
func (t Time) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface. The time is a quoted string in RFC 3339 format, with sub-second precision added if present.

自定义了序列化的结构

总结

枚举类型在 Go 里面的时候,用来表示连续的递增的值。

比如想表示颜色、比如像表示星期几

这种连续的值,一般都选择枚举类型来定义。

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

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

比如想自定义的颜色顺序:

const (
    Red = iota
    Orange
    Yellow
    Green
    Cyan
    Blue
    Purple
)

func main(){
        fmt.Println(Red,
        Orange,
        Yellow,
        Green,
        Cyan,
        Blue,
        Purple)
}

>>
0 1 2 3 4 5 6

当然提到枚举,考点不会这样考你,而是如下面的操作:

const (
    Red = iota
    Orange
    Yellow = iota + 10
    Green = iota
    Cyan
    Blue = 10
    Purple
)


问你各个值得多少?


0 1 12 3 4 10 10

<完>

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

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,865评论 6 13
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,211评论 0 4
  • 1 Object 对象 教程:https://wangdoc.com/javascript/stdlib/obje...
    智勇双全的小六阅读 1,573评论 0 0
  • 结束了10天的新加坡泰国之旅,有太多太多想说的了,本打算每天都写旅行日记,但是太累了,特别是前三天暴走新加坡。然后...
    吃橙啊啊啊阅读 112评论 0 0
  • 一、小括号,圆括号() 1、单小括号 () ①命令组。括号中的命令将会新开一个子shell顺序执行,所以括号中的变...
    北山学者阅读 1,499评论 0 2