当前位置: 首页 > 科技观察

学习Go中的时间处理_0

时间:2023-03-15 08:28:26 科技观察

作为程序员,我们经常需要处理时间。在Go中,标准库时间提供了相应的能力。本文将介绍时间库中的一些重要功能和方法,希望能帮助到遇到Go时间处理问题需要百度的童鞋们。处理时区问题在编程中,我们经常会遇到八小时时差问题。这是时区差异造成的,为了能够更好的解决,我们需要了解几个时间定义标准。GMT(格林威治标准时间),格林威治标准时间。GMT是根据地球的自转和公转来计算时间的。它规定太阳经过位于英国伦敦郊区的皇家格林威治天文台的时间是每天中午12点。GMT是以前的世界时间。UTC(CoordinatedUniversalTime),协调世界时。UTC比GMT更准确,它根据原子钟计算时间。在不需要精确到秒的情况下,可以考虑UTC=GMT。UTC是当前世界时间。从格林威治本初子午线开始,东为正,西为负。世界分为24个标准时区,相邻时区相差一小时。packagemainimport("fmt""time")funcmain(){fmt.Println(time.Now())}中国大陆使用东八时区标准时间,即北京时间CST,ChinaStandardTime。$gorunmain.go2022-07-1716:37:31.186043+0800CSTm=+0.000066647这是默认时区的结果,time.Now()的打印中会标注+0800CST.假设我们在洛杉矶时区,结果是什么?$TZ="America/Los_Angeles"gorunmain.go2022-07-1701:39:12.391505-0700PDTm=+0.000069514可以看到此时的结果是-0700PDT时间,也就是PDT(PacificDaylight时间)太平洋夏令时。由于时区差异,两次执行的时间结果相差15小时。注意,在使用Docker容器时,系统默认的时区是UTC时间(0时区),与我们实际需要的北京时间相差八个小时。这是导致八小时时差问题的经典场景。处理时区问题,可以详细查看src/time/zoneinfo_unix.go中initLocal()函数的加载逻辑。例如可以通过指定环境变量TZ、修改/etc/localtime文件等方式解决。因为时区问题很重要,所以在文章的第一部分进行了说明。下面开始介绍时间库的使用。时间即时time.Timetime库,核心对象是time.Time结构体。它定义如下,用于表示一个时刻。typeTimestruct{//wall和ext编码walltimeseconds,walltimenanoseconds,//和可选的单调时钟读数(以纳秒为单位)。walluint64extint64loc*Location}计算机在时间处理上主要涉及两种时钟。挂钟(walltime),又称时钟时间,用于指示具体的日期和时间。始终保证时间向前的单调时钟不存在让挂钟倒退的问题,因此非常适合测量连续的时间段。wall和ext字段用于以纳秒精度记录挂钟和单调时钟。该字段对应的数字与具体的年、月、日、时、分、秒等信息相关联,用于确定时间。loc字段记录时区位置,当loc为nil时,默认为UTC时间。因为time.Time用于表示具有纳秒精度的时间瞬间,所以在程序中它通常应该作为值而不是指针来存储和传递。即在时间变量或结构域中,我们应该使用time.Time而不是*time.Time。获取time.Time,我们可以使用Now函数获取当前本地时间funcNow()Time{}或者使用Date函数根据年月日等时间和时区参数获取指定时间,etc.funcDate(yearint,monthMonth,day,hour,min,sec,nsecint,loc*Location)Time{}将时间戳计算机世界,UTC时间January1,19700:00:00转换为Unix时间0.所谓时间instant就是转为Unix时间戳,即计算的是从Unix时间0到指定的instant之间经过的秒数、微秒数等。func(tTime)Unix()int64{}//自Unix时间0以来的秒数func(tTime)UnixMicro()int64{}//自Unix时间0以来的微秒func(tTime)UnixMilli()int64{}//毫秒sinceUnixtime0func(tTime)UnixNano()int64{}//自Unixtime0到获取基字段t:=time.Now()fmt.Println(t.Date())//2022July17fmt.Println(t.Year())//2022fmt.Println(t.Month())//七月fmt。Println(t.ISOWeek())//202228fmt.Println(t.Clock())//222156fmt.Println(t.Day())//17fmt.Println(t.Weekday())//周日fmt.Println(t.Hour())//22fmt.Println(t.Minute())//21fmt.Println(t.Second())//56fmt.Println(t.Nanosecond())//494313000fmt.Println(t.YearDay())//198durationtime.Durationdurationtime.Duration用来表示两个时间点time.Time之间经过的时间。它通过int64表示纳秒计数,可以表示的极限是大约290年。//Duration表示两个瞬间之间经过的时间//作为int64纳秒计数。该表示将//最大可表示持续时间限制为大约290年。类型Durationint64在Go中,持续时间只是一个纳秒数。如果持续时间等于1000000000,则表示1秒或1000毫秒或1000000微秒或1000000000纳秒。比如相隔1小时的两个时间瞬间time.Time值,它们之间的持续时间time.Duration值为1*60*60*1000*1000*1000Go的time包定义了这些持续时间常量值const(NanosecondDuration=1微秒=1000*纳秒毫秒=1000*微秒秒=1000*毫秒分=60*秒时=60*分)同时time.Duration提供了方法func(dDuration)可以获取的值每次粒度Nanoseconds()int64{}//纳秒func(dDuration)Microseconds()int64{}//微秒func(dDuration)Milliseconds()int64{}//毫秒func(dDuration)Seconds()float64{}//秒func(dDuration)Minutes()float64{}//分钟func(dDuration)Hours()float64{}//小时计算学习了时刻和持续时间后,让我们看看如何进行时间计算。func(tTime)Add(dDuration)Time{}添加函数增加/减少(d为正值表示增加,负值表示减少)time.Time的持续时间。我们可以为瞬时时间增加或减少指定的纳秒时间。func(tTime)Sub(uTime)Duration{}Sub函数返回两个时刻之间的持续时间。func(tTime)AddDate(yearsint,monthsint,daysint)Time{}AddDate函数根据年、月、日的维度递增/递减time.Time的值。当然,基于当前时间instanttime.Now()的计算是最常见的需求。因此,时间包还提供了以下方便的时间计算功能。funcSince(tTime)Duration{}Since函数是time.Now().Sub(t)的快捷方法。funcUntil(tTime)Duration{}Until函数是t.Sub(time.Now())的快捷方法。使用示例t:=time.Now()fmt.Println(t)//2022-07-1722:41:06.001567+0800CSTm=+0.000057466//时间增加1小时fmt.Println(t.Add(time.Hour*1))//2022-07-1723:41:06.001567+0800CSTm=+3600.000057466//时间增加15分钟fmt.Println(t.Add(time.Minute*15))//2022-07-1722:56:06.001567+0800CSTm=+900.000057466//时间增加10秒fmt.Println(t.Add(time.Second*10))//2022-07-1722:41:16.001567+0800CSTm=+10.000057466//将时间减少1小时fmt.Println(t.Add(-time.Hour*1))//2022-07-1721:41:06.001567+0800CSTm=-3599.999942534//减少15分钟时间fmt.Println(t.Add(-time.Minute*15))//2022-07-1722:26:06.001567+0800CSTm=-899.999942534//将时间减少10秒fmt。Println(t.Add(-time.Second*10))//2022-07-1722:40:56.001567+0800CSTm=-9.999942534time.Sleep(time.Second*5)t2:=time.Now()//计算从t到t2的持续时间fmt.Println(t2.Sub(t))//5.004318874s//1年后的时间t3:=t2.AddDate(1,0,0)//计算从t到当前持续时间fmt.Println(time.Since(t))//5.004442316s//计算从现在到明年的时长fmt.Println(time.Until(t3))//8759h59m59.999864s格式化时间在其他语言中一般使用通用时间模板来格式化时间比如Python,使用%Y代表年,%m代表月,%d代表日等等。但是,Go不同。它使用一个固定时间(注意其他时间不允许)作为布局模板,而这个固定时间就是Go语言的诞生时间。MonJan215:04:05MST2006格式化时间涉及到两个转换函数funcParse(layout,valuestring)(Time,error){}Parse函数用于将时间字符串按照其对应的layout转换为一个time.Time目的。func(tTime)Format(layoutstring)string{}Format函数用于将time.Time对象按照给定的布局转换为时间字符串。示例const(layoutISO="2006-01-02"layoutUS="January2,2006")date:="2012-08-09"t,_:=time.Parse(layoutISO,date)fmt.Println(t)//2012-08-0900:00:00+0000UTCfmt.Println(t.Format(layoutUS))//August9,2012在时间库中,Go提供了一些预定义的布局模板常量,可以直接带上使用。const(Layout="01/0203:04:05PM'06-0700"//参考时间,按数字顺序。ANSIC="MonJan_215:04:052006"UnixDate="MonJan_215:04:05MST2006"RubyDate="MonJan0215:04:05-07002006"RFC822="02Jan0615:04MST"RFC822Z="02Jan0615:04-0700"//RFC822withnumericzoneRFC850=“星期一,2006年1月2日15:04:05MST”RFC1123=“星期一,2006年1月2日15:04:05MST”RFC1123Z=“星期一,2006年1月2日15:04:05-0700”//RFC1123带数字区域RFC3339="2006-01-02T15:04:05Z07:00"RFC3339Nano="2006-01-02T15:04:05.999999999Z07:00"Kitchen="3:04PM"//方便的时间戳。Stamp="Jan_215:04:05"StampMilli="Jan_215:04:05.000"StampMicro="Jan_215:04:05.000000"StampNano="Jan_215:04:05.000000000")以下是我们可选的布局参数比较表年06/2006月01/1/Jan/Januaryday02/2/_2weekMon/Mondayhour03/3/15minute04/4second05/5millisecond.000/.999microsecond.000000/.999999纳秒.000000000/.999999999am/pmPM/pm时区MST时区时差-0700/-07/-07:00/Z0700/Z07:00时区转换文章开头介绍了时区issueif在代码中,我们需要获取同一时间的结果。不同时区的时间,我们可以使用它的In方法。func(tTime)In(loc*Location)Time{}它的使用很简单,直接看示例代码now:=time.Now()fmt.Println(now)//2022-07-1821:19:59.9636+0800CSTm=+0.000069242loc,_:=time.LoadLocation("UTC")fmt.Println(now.In(loc))//2022-07-1813:19:59.9636+0000UTCloc,_=time.LoadLocation("Europe/Berlin")fmt.Println(now.In(loc))//2022-07-1815:19:59.9636+0200CESTloc,_=time.LoadLocation("America/New_York")英尺。Println(now.In(loc))//2022-07-1809:19:59.9636-0400EDTloc,_=time.LoadLocation("Asia/Dubai")fmt.Println(now.In(loc))//2022-07-1817:19:59.9636+0400+04总结总的来说,时间库提供的时间处理函数和方法基本可以满足我们的需求。有意思的是,Go的时间格式转换必须要用Go的出生时间,确实很自恋。