Pgx: How do you set the timezone connection variable?

Created on 13 Mar 2019  ยท  11Comments  ยท  Source: jackc/pgx

I'm trying to set the session timezone variable that Postgres allows in the connection string but I'm not familiar how to do it using pgx. I tried to do the following below but it did not work.

runtimeParams := make(map[string]string)
runtimeParams["timezone"] = "UTC"

config := pgx.ConnConfig{
    Host:          host,
    Port:          port,
    User:          user,
    Password:      password,
    Database:      database,
    RuntimeParams: runtimeParams,
}

Most helpful comment

So, there are 2 ways to show pgx result's timestamptz in some requested timezone:

  1. applicatiion-wide, using TZ as described in time package sources
  2. per value, using time.In()

and this problem is not related with RuntimeParams["timezone"]

Am I right?

All 11 comments

Works for me.

package main

import (
    "log"
    "os"

    "github.com/jackc/pgx"
)

func main() {
    config, err := pgx.ParseURI(os.Getenv("DATABASE_URL"))
    if err != nil {
        log.Fatal(err)
    }

    if s := os.Getenv("PGX_TIMEZONE"); s != "" {
        config.RuntimeParams["timezone"] = s
    }

    conn, err := pgx.Connect(config)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    var tz string
    err = conn.QueryRow("select current_setting('TIMEZONE')").Scan(&tz)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("select current_setting('TIMEZONE') => ", tz)
}

Example runs:

jack@happy:~/go/src/github.com/jackc/pgx-520$ go run main.go 
2019/03/23 12:03:20 select current_setting('TIMEZONE') =>  localtime
jack@happy:~/go/src/github.com/jackc/pgx-520$ PGX_TIMEZONE=UTC go run main.go 
2019/03/23 12:03:31 select current_setting('TIMEZONE') =>  UTC
jack@happy:~/go/src/github.com/jackc/pgx-520$ PGX_TIMEZONE=Europe/Berlin go run main.go 
2019/03/23 12:03:43 select current_setting('TIMEZONE') =>  Europe/Berlin

Works for me.

How to get correct timestamp with this setting?

package main

import (
    "log"
    "os"
    "time"

    "github.com/jackc/pgx"
)

func main() {
    config, err := pgx.ParseURI(os.Getenv("DATABASE_URL"))
    if err != nil {
        log.Fatal(err)
    }

    if s := os.Getenv("PGX_TIMEZONE"); s != "" {
        config.RuntimeParams["timezone"] = s
    }

    conn, err := pgx.Connect(config)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    var tz string
    var tm time.Time
    err = conn.QueryRow("select current_setting('TIMEZONE'), now()").Scan(&tz, &tm)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("TIMEZONE: %s, NOW %v\n", tz, tm)
}

Shows:

$ go run main.go
2019/04/03 23:43:11 TIMEZONE: UTC, NOW 2019-04-03 23:43:11.741484 +0300 MSK
$ PGX_TIMEZONE=Europe/Berlin go run main.go
2019/04/03 23:43:12 TIMEZONE: Europe/Berlin, NOW 2019-04-03 23:43:12.097781 +0300 MSK

Meanwhile this gives correct timestamp (but not setting):

TZ=Europe/Berlin go run main.go
2019/04/03 22:43:12 TIMEZONE: UTC, NOW 2019-04-03 22:43:12.454271 +0200 CEST

That is because of different time zones in Go and PostgreSQL. The timestamp with time zone type in PostgreSQL actually doesn't have time zone information. It converts from the client time zone to UTC on the way in and from UTC to the client time zone on the way out. But all it is internally is an int64 of the number of microseconds from 2000-01-01 00:00:00 UTC. And that is what pgx gets over the wire from PostgreSQL (using the binary protocol which is the default).

If you want to display it in a different time zone in Go you will need to convert it there. (e.g. https://golang.org/pkg/time/#Time.In).

Yes, Time.In() allows to change TZ per var, but how to change all of them?

With postgresql clients like psql I can set timezone once and get all of timestamptz values in that timezone.
With pgx I've found TZ=.. go run but might be there is a way better?

The TZ environment variable is probably what you want then.

So, there are 2 ways to show pgx result's timestamptz in some requested timezone:

  1. applicatiion-wide, using TZ as described in time package sources
  2. per value, using time.In()

and this problem is not related with RuntimeParams["timezone"]

Am I right?

Correct.

Ok, thank you.
Sorry if my posts were an offtopic for this issue

That is because of different time zones in Go and PostgreSQL. The timestamp with time zone type in PostgreSQL actually doesn't have time zone information. It converts from the client time zone to UTC on the way in and from UTC to the client time zone on the way out. But all it is internally is an int64 of the number of microseconds from 2000-01-01 00:00:00 UTC. And that is what pgx gets over the wire from PostgreSQL (using the binary protocol which is the default).

If you want to display it in a different time zone in Go you will need to convert it there. (e.g. https://golang.org/pkg/time/#Time.In).

Does this mean that even if we explicitly query using AT TIME ZONE 'utc' i.e)

select valid_from AT TIME ZONE 'utc' FROM sometable;

pgx anyways scans and returns to me in my local timezone unless the appropriate environment variable is added?.

at time zone converts a timestamp with time zone to a timestamp without time zone in the specified zone.

e.g.

jack@[local:/private/tmp]:5432 jack=# select pg_typeof(now()),  pg_typeof(now() AT TIME ZONE 'utc');
        pg_typeof         โ”‚          pg_typeof
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 timestamp with time zone โ”‚ timestamp without time zone

Thanks @jackc , makes sense now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hgl picture hgl  ยท  6Comments

Oliver-Fish picture Oliver-Fish  ยท  4Comments

skipcloud picture skipcloud  ยท  4Comments

k1ng440 picture k1ng440  ยท  5Comments

Nokel81 picture Nokel81  ยท  6Comments