Go语言转换Chrome SQLite时间戳结果早于1601年的问题排查
Chrome时间戳转换异常问题解决
问题描述
尝试用Go语言读取Chrome本地SQLite数据库中的lastVisitTime字段(该字段为自1601年1月1日起的微秒数)并转换为本地时间,数据库读取的数值经第三方工具验证正确,但代码执行后得到的时间早于1601年,不符合预期。
代码示例
package main import ( "database/sql" "fmt" "time" _ "github.com/mattn/go-sqlite3" "github.com/local_library/comp" ) var ( dbPath = comp.Expanduser("~/Library/Application Support/Google/Chrome/Default/History") chromeEpochStart = time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC) ) const ( driverName = "sqlite3" tmpPath = "/tmp/History" query = ` SELECT last_visit_time FROM urls ORDER BY last_visit_time DESC LIMIT 5 ` ) func main() { // Copy to tmp to unlock err := comp.Copy(dbPath, tmpPath) comp.MustBeNil(err) db, err := sql.Open(driverName, tmpPath) comp.MustBeNil(err) rows, err := db.Query(query) comp.MustBeNil(err) for rows.Next() { var lastVisitTime int64 rows.Scan(&lastVisitTime) d := time.Duration(time.Microsecond * time.Duration(lastVisitTime)) t := chromeEpochStart.Add(d) fmt.Println(t, lastVisitTime) } err = rows.Close() comp.MustBeNil(err) err = rows.Err() comp.MustBeNil(err) }
程序输出
1439-07-05 20:00:21.462742384 +0000 UTC 13350512095172294 1439-07-05 19:58:20.377916384 +0000 UTC 13350511974087468 1439-07-05 19:57:58.539932384 +0000 UTC 13350511952249484 1439-07-05 19:57:48.539540384 +0000 UTC 13350511942249092 1439-07-05 19:52:09.587445384 +0000 UTC 13350511603296997
异常原因
time.Duration是Go中表示时间段的类型,底层为int64类型,单位是纳秒,其最大值约为292年(2^63-1纳秒)。而Chrome时间戳对应的时间跨度(从1601年到当前时间)超过了292年,导致计算time.Microsecond * time.Duration(lastVisitTime)时发生整数溢出,得到一个远小于预期的(甚至负数的)Duration值,最终调用Add方法后时间被错误地回退到1601年之前。
正确转换方式
避免使用time.Duration处理超长时间跨度,通过Chrome时间戳与Unix时间戳的固定偏移量来转换,具体实现如下:
修正后的代码片段
package main import ( "database/sql" "fmt" "time" _ "github.com/mattn/go-sqlite3" "github.com/local_library/comp" ) var ( dbPath = comp.Expanduser("~/Library/Application Support/Google/Chrome/Default/History") // Chrome epoch(1601-01-01 UTC) 与 Unix epoch(1970-01-01 UTC) 的时间差,单位:秒 chromeToUnixOffset = 11644473600 ) const ( driverName = "sqlite3" tmpPath = "/tmp/History" query = ` SELECT last_visit_time FROM urls ORDER BY last_visit_time DESC LIMIT 5 ` ) // convertChromeTimestamp 将Chrome微秒时间戳转换为UTC时间 func convertChromeTimestamp(ts int64) time.Time { sec := ts / 1000000 // 将微秒转为秒 nsec := (ts % 1000000) * 1000 // 剩余微秒转为纳秒 unixSec := sec - int64(chromeToUnixOffset) return time.Unix(unixSec, nsec).UTC() } func main() { // Copy to tmp to unlock err := comp.Copy(dbPath, tmpPath) comp.MustBeNil(err) db, err := sql.Open(driverName, tmpPath) comp.MustBeNil(err) rows, err := db.Query(query) comp.MustBeNil(err) for rows.Next() { var lastVisitTime int64 rows.Scan(&lastVisitTime) t := convertChromeTimestamp(lastVisitTime) fmt.Println(t, lastVisitTime) } err = rows.Close() comp.MustBeNil(err) err = rows.Err() comp.MustBeNil(err) }
原理说明
- Chrome的时间戳起始点(1601-01-01 UTC)与Unix时间戳起始点(1970-01-01 UTC)的固定差值为
11644473600秒,这是预先计算好的常量。 - 将Chrome的微秒时间戳拆分为秒和纳秒部分,减去固定偏移量后得到标准Unix时间戳,再通过
time.Unix方法转换为time.Time对象,避免了time.Duration的溢出问题。
内容的提问来源于stack exchange,提问作者Chris Redford
相关产品推荐
相关产品推荐

