Skip to content

Commit b908dcb

Browse files
authored
Set session timezone to local timezone on startup (#1482)
* Set session timezone to local timezone on startup Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> * Update changelog Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> * Unconditionally import tzlocal Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> * Update authors Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> * Format Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> * add warning message for conflicting timezone configs Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> * handle None when setting timezone Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> * feat: add config for use_local_timezone, update error messages Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com> --------- Signed-off-by: maximsmol <1472826+maximsmol@users.noreply.github.com>
1 parent 7258931 commit b908dcb

File tree

6 files changed

+78
-1
lines changed

6 files changed

+78
-1
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ Contributors:
137137
* Chris Rose (offbyone/offby1)
138138
* Mathieu Dupuy (deronnax)
139139
* Chris Novakovic
140+
* Max Smolin (maximsmol)
140141
* Josh Lynch (josh-lynch)
141142
* Fabio (3ximus)
142143
* Doug Harris (dougharris)

changelog.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
Dev
2+
==================
3+
4+
Features
5+
--------
6+
* The session time zone setting is set to the system time zone by default
7+
18
4.2.0 (2025-03-06)
29
==================
310

pgcli/main.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from zoneinfo import ZoneInfoNotFoundError
12
from configobj import ConfigObj, ParseError
23
from pgspecial.namedqueries import NamedQueries
34
from .config import skip_initial_comment
@@ -27,6 +28,7 @@
2728
from cli_helpers.utils import strip_ansi
2829
from .explain_output_formatter import ExplainOutputFormatter
2930
import click
31+
import tzlocal
3032

3133
try:
3234
import setproctitle
@@ -1600,9 +1602,9 @@ def cli(
16001602
if list_databases or ping_database:
16011603
database = "postgres"
16021604

1605+
cfg = load_config(pgclirc, config_full_path)
16031606
if dsn != "":
16041607
try:
1605-
cfg = load_config(pgclirc, config_full_path)
16061608
dsn_config = cfg["alias_dsn"][dsn]
16071609
except KeyError:
16081610
click.secho(
@@ -1631,6 +1633,55 @@ def cli(
16311633
else:
16321634
pgcli.connect(database, host, user, port)
16331635

1636+
if "use_local_timezone" not in cfg["main"] or cfg["main"].as_bool(
1637+
"use_local_timezone"
1638+
):
1639+
server_tz = pgcli.pgexecute.get_timezone()
1640+
1641+
def echo_error(msg: str):
1642+
click.secho(
1643+
"Failed to determine the local time zone",
1644+
err=True,
1645+
fg="yellow",
1646+
)
1647+
click.secho(
1648+
msg,
1649+
err=True,
1650+
fg="yellow",
1651+
)
1652+
click.secho(
1653+
f"Continuing with the default time zone as preset by the server ({server_tz})",
1654+
err=True,
1655+
fg="yellow",
1656+
)
1657+
click.secho(
1658+
"Set `use_local_timezone = False` in the config to avoid trying to override the server time zone\n",
1659+
err=True,
1660+
dim=True,
1661+
)
1662+
1663+
local_tz = None
1664+
try:
1665+
local_tz = tzlocal.get_localzone_name()
1666+
1667+
if local_tz is None:
1668+
echo_error("No local time zone configuration found\n")
1669+
else:
1670+
click.secho(
1671+
f"Using local time zone {local_tz} (server uses {server_tz})",
1672+
fg="green",
1673+
)
1674+
click.secho(
1675+
"Use `set time zone <TZ>` to override, or set `use_local_timezone = False` in the config",
1676+
dim=True,
1677+
)
1678+
1679+
pgcli.pgexecute.set_timezone(local_tz)
1680+
except ZoneInfoNotFoundError as e:
1681+
# e.args[0] is the pre-formatted message which includes a list
1682+
# of conflicting sources
1683+
echo_error(e.args[0])
1684+
16341685
if list_databases:
16351686
cur, headers, status = pgcli.pgexecute.full_databases()
16361687

pgcli/pgclirc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ enable_pager = True
191191
# Use keyring to automatically save and load password in a secure manner
192192
keyring = True
193193

194+
# Automatically set the session time zone to the local time zone
195+
# If unset, uses the server's time zone, which is the Postgres default
196+
use_local_timezone = True
197+
194198
# Custom colors for the completion menu, toolbar, etc.
195199
[colors]
196200
completion-menu.completion.current = 'bg:#ffffff #000000'

pgcli/pgexecute.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,3 +881,16 @@ def casing(self):
881881

882882
def explain_prefix(self):
883883
return "EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) "
884+
885+
def get_timezone(self) -> str:
886+
query = psycopg.sql.SQL("show time zone")
887+
with self.conn.cursor() as cur:
888+
cur.execute(query)
889+
return cur.fetchone()[0]
890+
891+
def set_timezone(self, timezone: str):
892+
query = psycopg.sql.SQL("set time zone {}").format(
893+
psycopg.sql.Identifier(timezone)
894+
)
895+
with self.conn.cursor() as cur:
896+
cur.execute(query)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies = [
4040
# task manager. Also setproctitle is a hard dependency to install in Windows,
4141
# so we'll only install it if we're not in Windows.
4242
"setproctitle >= 1.1.9; sys_platform != 'win32' and 'CYGWIN' not in sys_platform",
43+
"tzlocal >= 5.2",
4344
]
4445
dynamic = ["version"]
4546

0 commit comments

Comments
 (0)