如何设置pgx从数据库获取UTC时间?

4

我正在使用EntPgx

在Postgres中创建的列是:

used_at timestamp with time zone NOT NULL,

Postgres中的值是在没有时区信息的情况下保存的(使用UTC时间):
2022-06-30 22:49:03.970913+00

使用以下查询:

show timezone

我得到:

Etc/UTC

使用 pgx stdlib 连接到 Ent 后,我获得了以下值:

2022-07-01T00:49:03.970913+02:00

使用pgdriver/pq,我从数据库中获取UTC值。

如何设置pgx以获取UTC值?

我也尝试使用此连接字符串和以下代码:

import (
    "database/sql"
    _ "github.com/jackc/pgx/v4/stdlib"
)

conn, err := sql.Open("pgx", "postgres://postgres:postgres@localhost/project?sslmode=disable&timezone=UTC")
//handle err

问题仍然存在。

我需要一种方法从数据库中获取UTC值(这些值已经存储在数据库中)。


下次请在上游项目跟踪器中发布相关问题的链接或您发布的评论中,这样下一个想要帮助的人就不会自己发现了。 - kostix
2
数据或结果没有问题,+00 是一个时区,或者更确切地说是一个偏移量,+00:00。结果在不同的时区中是完全相同的值,可能是客户端格式化的结果。驱动程序不会随意选择东欧偏移量。 - Panagiotis Kanavos
2个回答

4

timezone不是有效的参数关键字

但是,您可以使用options关键词,在连接开始时指定要发送到服务器的命令行选项。只需记住需要百分号编码其中的值。

设置TimeZone的示例:

package main

import (
    "context"
    "fmt"
    "github.com/jackc/pgx/v4"
)

func main() {
    ctx := context.Background()

    c1, err := pgx.Connect(ctx, "postgres:///?sslmode=disable")
    if err != nil {
        panic(err)
    }
    defer c1.Close(ctx)

    // sends "-c TimeZone=UTC" to server at connection start
    c2, err := pgx.Connect(ctx, "postgres:///?sslmode=disable&options=-c%20TimeZone%3DUTC")
    if err != nil {
        panic(err)
    }
    defer c2.Close(ctx)

    var tz1, tz2 string
    if err := c1.QueryRow(ctx, "SHOW timezone").Scan(&tz1); err != nil {
        panic(err)
    }
    if err := c2.QueryRow(ctx, "SHOW timezone").Scan(&tz2); err != nil {
        panic(err)
    }
    fmt.Println(tz1)
    fmt.Println(tz2)
}

Europe/Prague
UTC

然而,这只是强制连接的时区,对于从数据库中读取的时间戳本身的解析在pgx中似乎没有影响。实际上,它似乎直接或间接地依赖于主机机器的本地时区。为了确认这一点,您可以将全局变量time.Local更新为UTC并观察差异。

    // ...

    var t1, t2 time.Time
    if err := c1.QueryRow(ctx, "select now()::timestamptz").Scan(&t1); err != nil {
        panic(err)
    }
    if err := c2.QueryRow(ctx, "select now()::timestamptz").Scan(&t2); err != nil {
        panic(err)
    }
    fmt.Println(t1)
    fmt.Println(t2)

    // explicitly set Local to UTC
    time.Local = time.UTC
    if err := c1.QueryRow(ctx, "select now()::timestamptz").Scan(&t1); err != nil {
        panic(err)
    }
    if err := c2.QueryRow(ctx, "select now()::timestamptz").Scan(&t2); err != nil {
        panic(err)
    }
    fmt.Println(t1)
    fmt.Println(t2)
}

Europe/Prague
UTC
2022-06-27 17:18:13.189557 +0200 CEST
2022-06-27 17:18:13.190047 +0200 CEST
2022-06-27 15:18:13.190401 +0000 UTC
2022-06-27 15:18:13.190443 +0000 UTC

出于明显的原因,我会避免上述方法。如果pgx没有提供一种配置默认位置以解析时间戳的方式,则我能想到的下一个最佳选项是使用自定义的time.Time类型。

    // ...

    var t1, t2 time.Time
    if err := c1.QueryRow(ctx, "select now()::timestamptz").Scan(&t1); err != nil {
        panic(err)
    }
    if err := c2.QueryRow(ctx, "select now()::timestamptz").Scan(&t2); err != nil {
        panic(err)
    }
    fmt.Println(t1)
    fmt.Println(t2)

    var tt1, tt2 myTime
    if err := c1.QueryRow(ctx, "select now()::timestamptz").Scan(&tt1); err != nil {
        panic(err)
    }
    if err := c2.QueryRow(ctx, "select now()::timestamptz").Scan(&tt2); err != nil {
        panic(err)
    }
    fmt.Println(tt1)
    fmt.Println(tt2)
}

type myTime struct {
    time.Time
}

func (tt *myTime) Scan(src interface{}) error {
    if t, ok := src.(time.Time); ok {
        tt.Time = t.In(time.UTC)
        return nil
    }
    return fmt.Errorf("myTime: unsupported type %T", src)
}

Europe/Prague
UTC
2022-06-27 17:26:45.94049 +0200 CEST
2022-06-27 17:26:45.940959 +0200 CEST
2022-06-27 15:26:45.941321 +0000 UTC
2022-06-27 15:26:45.941371 +0000 UTC

非常感谢@mkopriva。如果我需要更改数据库时区,您的提示非常有用。我的数据库已经在UTC上了。问题是pgx返回的时间格式不是UTC格式,而是以“+02:00”结尾的格式。 - Fred Hors
我也尝试了这个:sql.Open("pgx", connURL+"&options=-c%20TimeZone%3DUTC")。它是相同的,没有UTC值。 - Fred Hors
@FredHors 我已经添加了一些更新。 - mkopriva
越多自定义代码,问题就越多!pgx的行为真是疯狂! - Fred Hors

0
您可以全球配置您的应用程序使用UTC。
package main

import "time"

func main() {

    location, _ := time.LoadLocation("UTC")
    time.Local = location
    
    // setup db connection
    // ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接