GoLang 中带时区的时间戳

时间:2021-07-24 00:28:26

标签: database postgresql datetime go driver

我进行 HTTP 调用并从较大的 json 对象解组 createdTimestamp 字段:

CreatedTimestamp string `json:"createdTimestamp"`

我从 createdTimestamp 的 HTTP 调用中收到的示例是:"2021-07-19T18:51:23"

它不会自动将其转换为 time.Time,因此它真正接受的唯一类型是一个字符串,该字符串在 Postgresql 中的类型更改为 timestamp with timezone 之前一直有效 < 根据 Postgresql 看起来像这样数据库控制台 >(yyyy-MM-dd HH:mm:ss.ffffff)。和以前一样,它是 without timezone。 我能够为 time.Time 对象实现自定义解组方法,甚至可以对其进行格式化,以便我可以输出类似 2021-07-19 18:51:23.+00 的内容,但这不会被 postgres 接受。

我在尝试插入时看到的确切错误(通过 GoLang、postgres 驱动程序)是:pq: invalid input syntax for type timestamp with time zone: "" 在我尝试使用 db.Exec -> db 类型为 *sql.DB 执行插入后出现此错误。

我正在 Go 中执行此操作,任何帮助将不胜感激!

解组 Json 的代码

type CustomTime struct {
    time.Time
}

func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
    timeToInsertIntoDb := strings.Trim(string(b), "\"")
    if timeToInsertIntoDb == "null" {
        ct.Time = time.Time{}
        return
    }

    timeToInsertIntoDb = timeToInsertIntoDb + "Z"
    ct.Time, err = time.Parse(time.RFC3339, timeToInsertIntoDb)
    return
}

对于格式化,它取决于输出,但我确实收到了任何格式的输出。所以如果我这样做,CustomTime.Time.Format(2006-02-01 15:04:05.-07) 我会得到 2021-07-19 18:51:23.+00

的输出

尽管此时,我什至不确定 Timestamp with Timezone 所需的确切格式,但关于 Golang Postgres 驱动程序的文档并不多。

如果需要更多信息,请询问。我正在尽力组织这个问题。

编辑 1


按照建议,我尝试将“Z”附加到 http 调用给出的时间戳。使用 time.RFC3339 进行解析后,我得到了 2021-07-19T18:51:23Z 的时间 - 这仍然失败(给出了上述相同的语法错误)。通过这种解析尝试了几种不同的方法。用我上面所说的方式格式化它,并用它的 .String() 方法格式化,它会给出 2021-07-20 18:51:23 +0000 UTC。都失败了 pq: invalid input syntax for type timestamp with time zone: ""

解组期间的代码更改:

func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
    timeToInsertIntoDb := strings.Trim(string(b), "\"")
    if timeToInsertIntoDb == "null" {
        ct.Time = time.Time{}
        return
    }

    timeToInsertIntoDb = timeToInsertIntoDb + "Z"
    ct.Time, err = time.Parse(time.RFC3339, timeToInsertIntoDb)
    return
}

编辑 2

另一件要提及的事情是,我使用带有 "database/sql""github.com/lib/pq" 包作为用于数据库连接的 Postgres 驱动程序。 这就是错误来自 pq 的原因。只是想澄清一下,因为我知道其他人正在使用 gorm。我可以写入其他表,我猜只是这个表具有时区 Postgres 列的时间戳。

我正在与 db.Exec("INSERT INTO db (created_timestamp) VALUES ($1)", obj.createdTimestamp.Time 通话 我试过将它作为字符串传递(这是我之前在工作时所做的),但现在这就是我所处的位置,因为人们说最好传递一个 time.Time 变量。

2 个答案:

答案 0 :(得分:4)

如果您检查提供的时间格式 ctlayout 与时间格式标准 RFC3339 紧密匹配

const ctLayout = "2006-01-02T15:04:05"
const RFC3339  = "2006-01-02T15:04:05Z07:00"

从此blogpost

<块引用>

在 RFC 3339 中,我们也可以从格式中知道时区。它 以“Z”语法显示。 “Z”表示UTC+0。 “Z”代表祖鲁语 时区与 GMT 或 UTC 相同 (https://stackoverflow.com/a/9706777/4075313)。所以如果我们把 Z 放在 DateTime,表示它的时区是UTC+0。

如果我们更改传入时间并在末尾附加 'Z',则时间解析器应该会得到满足。

这是重构的代码。您可以在 playground 上找到工作代码。

        timeToInsertInDB := "2006-01-02T15:04:05" + "Z"

        testTime, err := time.Parse(time.RFC3339, timeToInsertInDB)
        if err != nil {
                fmt.Println(err) 
        }

注意:- 更详细的时区示例

2019-10-12T07:20:50.52Z      (UTC+0)
2019-10-12T07:20:50.52+00:00 (UTC+0)
2019-10-12T14:20:50.52+07:00 (UTC+7)
2019-10-12T03:20:50.52-04:00 (UTC-4)

postgres 驱动程序期望 time.Time。如果解析成功,驱动程序应该能够插入数据库以及解组模型结构中的响应。在 playground 上查看此示例。

编辑 1

根据 OP 的编辑,我更改了使用的驱动程序,我尝试使用 database/sql 包和 github.com/lib/pq 但无法重现该问题。插入和选择工作完全正常。 playground

正如 Brits 所提到的,这很可能是因为您的代码中存在逻辑错误(但无法确认这一点,因为您没有共享代码)。

答案 1 :(得分:2)

你的问题很难回答,因为它目前没有足够的信息让我复制这个问题。

如评论中所述,您可以并且应该在将时间戳插入数据库时​​传递 time.Time。这样做意味着该库负责以适当的格式将时间戳传递给服务器(如果您移动到例如 SQL Server,您的代码可能会继续工作)。

如果您传递的是 time.Time,那么该时间的来源将变得无关紧要。这意味着您的问题根本不需要提及从 JSON 的转换 - 只需确保您仔细检查此转换是否成功并且您将 time.Time 传递给 Postgres。

下面的代码演示了这一点(并且在我使用 Postgres 13.1 时成功完成) - 此代码使用 Shailesh Suryawanshi 建议的技术从 JSON 转换:

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
    "time"
)

func main() {
    db, err := sql.Open("postgres", "DSN goes here")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    // Create the table (would not normally do this here but this means the demo is self contained)
    _, err = db.Exec("create temporary table db(created_timestamp timestamp with time zone)")
    if err != nil {
        panic(err)
    }

    jsonTS := `2021-07-19T18:51:23`
    ts, err := time.Parse(time.RFC3339, jsonTS+"Z")
    if err != nil {
        panic(err)
    }

    _, err = db.Exec("INSERT INTO db (created_timestamp) VALUES ($1)", ts)
    if err != nil {
        panic(err)
    }

    // Test retrieving the value
    rows, err := db.Query("select * from db")
    if err != nil {
        panic(err)
    }
    defer rows.Close()
    for rows.Next() {
        var t time.Time
        err = rows.Scan(&t)
        if err != nil {
            panic(err)
        }
        fmt.Println(t)
    }

    fmt.Println("Complete")
}

我可以通过运行 db.Exec("INSERT INTO db (created_timestamp) VALUES ($1)", "") 来复制您的问题;完整的错误是:

panic: pq: invalid input syntax for type timestamp with time zone: ""

您的问题可能是由 Postgres 配置中的某些内容引起的;对您的服务器运行上述代码的修改版本将使您能够确认是否是这种情况。但是,根据上述情况,我认为最可能的原因是您的代码中存在逻辑错误(但无法确认,因为您没有共享代码)。

注意:lib/pq readme 声明:“我们建议使用积极维护的 pgx。”

相关问题