You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
}

原理说明

  1. Chrome的时间戳起始点(1601-01-01 UTC)与Unix时间戳起始点(1970-01-01 UTC)的固定差值为11644473600秒,这是预先计算好的常量。
  2. 将Chrome的微秒时间戳拆分为秒和纳秒部分,减去固定偏移量后得到标准Unix时间戳,再通过time.Unix方法转换为time.Time对象,避免了time.Duration的溢出问题。

内容的提问来源于stack exchange,提问作者Chris Redford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 13:07:03