在聊时间这个话题之前我们先了解两个概念:墙上时钟和单调时钟
墙上时钟:也称为墙上时间。大多是1970年1月1日(UTC)以来的秒数和毫秒数。墙上时间可以和NTP(Network Time Protocal,网络时间协议)同步,但是如果本地时钟远远快于NTP服务器,则强制重置之后会跳到先前某个时间点。
单调时钟:机器大多有自己的石英晶体振荡器,并将其作为计时器。单调时钟的绝对值没有任何意义,根据操作系统和语言的不同,单调时钟可能在程序开始时设为0、或在计算机启动后设为0等等。但是通过比较同一台计算机上两次单调时钟的差,可以获得相对准确的时间间隔。
在go 1.9之前,记录比较简单,就是1-1-1 00:00:00 到现在的整数s和ns数,以及时区数据。也就是墙上时钟
type Time struct {
// sec gives the number of seconds elapsed since
// January 1, year 1 00:00:00 UTC.
sec int64
// nsec specifies a non-negative nanosecond
// offset within the second named by Seconds.
// It must be in the range [0, 999999999].
nsec int32
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// The nil location means UTC.
// All UTC times are represented with loc==nil, never loc==&utcLoc.
loc *Location
}
在1.9之后记录了墙上时钟和单调时钟,wall和ext共同记录了时间,但是分为两种情况:
type Time struct {
wall uint64
ext int64
loc *Location
}
一种是没有记录单调时钟(比如是通过字符串解析得到的时间),另一种是记录了单调时钟(比如通过Now)。wall的第一位是一个标记位:
单调时钟的计算逻辑如下
var startNano = 0
func init(){
startNano = runtimeNano()
}
runtimeNano() - startNano
Time其实是实现了String函数的,因此可以print,打印出的时间格式为“年月日时分秒.小数秒” 精度为ns;后面还带着"m=±<value>",表示单调时钟的s数表示,小数点后精度到ns
func (t Time) String() string {
// String returns the time formatted using the format string
// "2006-01-02 15:04:05.999999999 -0700 MST"
//
// If the time has a monotonic clock reading, the returned string
// includes a final field "m=±<value>", where value is the monotonic
// clock reading formatted as a decimal number of seconds.
m1, m2 := m2/1e9, m2%1e9
m0, m1 := m1/1e9, m1%1e9
if m0 != 0 {
buf = appendInt(buf, int(m0), 0)
wid = 9
}
buf = appendInt(buf, int(m1), wid)
buf = append(buf, '.')
buf = appendInt(buf, int(m2), 9)
}
了解完golang的时间格式表示,我们过来看下mysql的时间格式表示:
总结下,也就是说常用的5.7版本,时间戳只能存到2038年,精度是秒,但是只需要4个字节,DATETIME存储的时间长度为5到8个字节,精度是微秒。
那么问题来了,当我们用golang驱动写mysql和从mysql查数据的时候,精度是什么样子的呢?显然写数据的时候会丢失精度从ns圆整到us
result, err := db.Exec("insert into time_test(`ctime`) values(?)", time.Now())
然后查询会得到
'2023-02-21 22:55:39.980742 +0800 CST m=+0.005420710'
元整发生在什么时候呢?在github.com/go-sql-driver/mysql 1.5.0版本和以前会在驱动里将时间元整到ms,但是1.6.0版本不再元整
https://github.com/go-sql-driver/mysql/commit/fe2230a8b20cee1e48f7b75a9b363def5f950ba0
就导致了一个有趣的现象,在mysql的各个版本中,因为mysql在处理时间参数的时候做了精度的元整,如果在datetime字段上加了索引,即使传了精度为ns的时间,也会走索引。但是对于marindb,如果传入的时间是ns精度,刚好把mysql驱动由1.5.0升级到了1.6.0会导致索引失效。
本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!