【问题标题】:Timestamp with Timezone in GoLangGoLang中带有时区的时间戳
【发布时间】:2021-07-24 00:28:26
【问题描述】:

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

CreatedTimestamp string `json:"createdTimestamp"`

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

它不会自动将其转换为time.Time,因此它真正接受的唯一类型是一个字符串,该类型一直有效,直到 Postgresql 中的类型更改为timestamp with timezone @ 987654327@。和以前一样是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


按照建议,我尝试在 http 调用给出的时间戳上附加一个“Z”。在使用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: ""

Unmarshal 期间的代码更改:

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 变量。

【问题讨论】:

  • 如果您能够成功转换为time.Time,然后将其作为参数传入(请参阅this answer)。请注意,您问题中的示例不一致 - createdTimestamp 是否包含时区?
  • @Brits,因此不包括时区。上面列出了我在 HTTP 调用中得到的数据,但我得到的数据是 UTC(代表我的现有知识),我一直试图让它存储在那个时区内。我试图传递time.Time,但这也行不通。请参阅Edit 1——在我刚刚尝试使用不同的time.Parse 时,它有更多详细信息。
  • 您面临的错误与 postgre 相关。我建议你给我们一个可重现的源代码,包括 sql 查询和执行,就像 Shailesh Suryawanshi 用户在他的帖子中所做的那样。这真的会帮助人们做对。你可能也想分享 http 处理程序代码,我们永远不知道。
  • @thatdevop 如果您能够成功地将CreatedTimestamp 转换为time.Time,那么时间的来源是无关紧要的,您的问题应该是“我如何使用time.Time 与一个 Postgres insert'(并提供一个 minimal reproducible example
  • @Brits 从技术上讲,它现在确实有一个时间戳,因为我在解组期间添加了“Z”,然后将其解析为RFC3339。它是time.Time 类型,我通过在运行db.Exec 之前记录类型来确保这一点。它曾经可以工作,实际上我认为数据库没有任何变化。不知道这里发生了什么。

标签: database postgresql datetime go driver


【解决方案1】:

如果您检查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

正如英国人所说,这很可能是您的代码中的逻辑错误(但由于您没有共享代码,因此无法确认)。

【讨论】:

  • 感谢您的回复!我尝试在解组期间附加“Z”,它允许我使用time.RFC3339 进行解析,这绝对是向前迈出的一步。如果我当时做一个.String(),我会得到类似2021-07-19 18:51:23 +0000 UTC的东西 - 但是当我尝试插入表格时,我仍然得到错误:pq: invalid input syntax for type timestamp with time zone: ""我不确定RFC3339是否是格式pq 期待。我也找不到相关文档。
  • 我还尝试使用.format(time.RFC3339) 传递,这意味着我正在传递2021-07-19T18:51:23Z,但这给了我上面所述的语法错误
  • @thatdevop 我在本地尝试过,对我的游乐场 play.golang.org/p/I8U-K1AT-bH 来说效果很好。 postgres 驱动程序只需要time.Time 格式。请检查最后的select语句。
  • 这很奇怪,老实说我认为它应该可以工作,但转换肯定是成功的。我试过以不同形式的createdTimestamp.Time 传递,比如传递createdTimestamp.Time.Format(time.RFC3339) 等等。我只是不断收到通用的无效时间戳错误。
【解决方案2】:

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

如 cmets 中所述,在将时间戳插入数据库时​​,您可以并且应该传递 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。”

【讨论】:

    猜你喜欢
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 2021-02-08
    • 2019-11-20
    • 2021-09-22
    相关资源
    最近更新 更多