前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang时间和mysql时间表示

golang时间和mysql时间表示

作者头像
golangLeetcode
发布2023-03-01 16:22:17
4.4K0
发布2023-03-01 16:22:17
举报
文章被收录于专栏:golang算法架构leetcode技术php

在聊时间这个话题之前我们先了解两个概念:墙上时钟和单调时钟

墙上时钟:也称为墙上时间。大多是1970年1月1日(UTC)以来的秒数和毫秒数。墙上时间可以和NTP(Network Time Protocal,网络时间协议)同步,但是如果本地时钟远远快于NTP服务器,则强制重置之后会跳到先前某个时间点。

单调时钟:机器大多有自己的石英晶体振荡器,并将其作为计时器。单调时钟的绝对值没有任何意义,根据操作系统和语言的不同,单调时钟可能在程序开始时设为0、或在计算机启动后设为0等等。但是通过比较同一台计算机上两次单调时钟的差,可以获得相对准确的时间间隔。

在go 1.9之前,记录比较简单,就是1-1-1 00:00:00 到现在的整数s和ns数,以及时区数据。也就是墙上时钟

代码语言:javascript
复制
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共同记录了时间,但是分为两种情况:

代码语言:javascript
复制
type Time struct {
   wall uint64
   ext  int64
   loc *Location
}

一种是没有记录单调时钟(比如是通过字符串解析得到的时间),另一种是记录了单调时钟(比如通过Now)。wall的第一位是一个标记位:

  1. 如果为1,则表示记录了单调时钟。则wall的2-34(闭区间)位记录了从1885-1-1到现在的秒数,最后30位记录了纳秒数。而ext记录了从程序开始运行到现在经过的单调时钟数。单位nanoseconds
  2. 如果为0,则表示没有记录单调时钟。则wall的2-34(闭区间)位全部为0(最后30位记录了纳秒数)。而ext记录了从1-1-1 00:00:00到现在经过的秒数。

单调时钟的计算逻辑如下

代码语言:javascript
复制
 var startNano = 0
 
 func init(){
     startNano = runtimeNano()
 }
 
 runtimeNano() - startNano

Time其实是实现了String函数的,因此可以print,打印出的时间格式为“年月日时分秒.小数秒” 精度为ns;后面还带着"m=±<value>",表示单调时钟的s数表示,小数点后精度到ns

代码语言:javascript
复制
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的时间格式表示:

  1. MySQL DATETIME存储包含日期和时间的值。从DATETIME列查询数据时,MySQL会以以下格式显示DATETIME值:YYYY-MM-DD HH:MM:SS。默认情况下,DATETIME的值范围为1000-01-01 00:00:00至9999-12-31 23:59:59。DATETIME使用5个字节进行存储。另外,DATETIME值可以包括格式为YYYY-MM-DD HH:MM:SS [.fraction]例如:2017-12-20 10:01:00.999999的尾数有小数秒。 当包含小数秒精度时,DATETIME值需要更多存储2017-12-20 10:01:00.999999需要8个字节,2015-12-20 10:01:00需要5个字节,3个字节为.999999,而2017-12-20 10:01:00.9只需要6个字节,小数秒精度为1字节。在MySQL 5.6.4之前,DATETIME值需要8字节存储而不是5个字节。
  2. TIMESTAMP需要4个字节,而DATETIME需要5个字节。 TIMESTAMP和DATETIME都需要额外的字节,用于分数秒精度。 TIMESTAMP范围从1970-01-01 00:00:01 UTC到2038-01-19 03:14:07 UTC。 如果要存储超过2038的时间值,则应使用DATETIME而不是TIMESTAMP。

总结下,也就是说常用的5.7版本,时间戳只能存到2038年,精度是秒,但是只需要4个字节,DATETIME存储的时间长度为5到8个字节,精度是微秒。

那么问题来了,当我们用golang驱动写mysql和从mysql查数据的时候,精度是什么样子的呢?显然写数据的时候会丢失精度从ns圆整到us

代码语言:javascript
复制
  result, err := db.Exec("insert into time_test(`ctime`) values(?)", time.Now())

然后查询会得到

代码语言:javascript
复制
'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版本不再元整

代码语言:javascript
复制
https://github.com/go-sql-driver/mysql/commit/fe2230a8b20cee1e48f7b75a9b363def5f950ba0

就导致了一个有趣的现象,在mysql的各个版本中,因为mysql在处理时间参数的时候做了精度的元整,如果在datetime字段上加了索引,即使传了精度为ns的时间,也会走索引。但是对于marindb,如果传入的时间是ns精度,刚好把mysql驱动由1.5.0升级到了1.6.0会导致索引失效。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档