Skip to content

Commit 4770d8f

Browse files
committed
Adding column_date_formats config option (#1402)
Allows per-column formatting for date, time, datetime like columns. Bumping cli_helpers to 2.4.0 for the timestamp formatting preprocessor.
1 parent b4bec22 commit 4770d8f

File tree

6 files changed

+72
-3
lines changed

6 files changed

+72
-3
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ Contributors:
139139
* Chris Novakovic
140140
* Josh Lynch (josh-lynch)
141141
* Fabio (3ximus)
142+
* Doug Harris (dougharris)
142143

143144
Creator:
144145
--------

changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Features
66
* Add a `--ping` command line option; allows pgcli to replace `pg_isready`
77
* Changed the packaging metadata from setup.py to pyproject.toml
88
* Add bash completion for services defined in the service file `~/.pg_service.conf`
9+
* Added support for per-column date/time formatting using `column_date_formats` in config
910

1011
Bug fixes:
1112
----------

pgcli/main.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
from typing import Optional
2020

2121
from cli_helpers.tabular_output import TabularOutputFormatter
22-
from cli_helpers.tabular_output.preprocessors import align_decimals, format_numbers
22+
from cli_helpers.tabular_output.preprocessors import (
23+
align_decimals,
24+
format_numbers,
25+
format_timestamps,
26+
)
2327
from cli_helpers.utils import strip_ansi
2428
from .explain_output_formatter import ExplainOutputFormatter
2529
import click
@@ -111,12 +115,13 @@
111115

112116
OutputSettings = namedtuple(
113117
"OutputSettings",
114-
"table_format dcmlfmt floatfmt missingval expanded max_width case_function style_output max_field_width",
118+
"table_format dcmlfmt floatfmt column_date_formats missingval expanded max_width case_function style_output max_field_width",
115119
)
116120
OutputSettings.__new__.__defaults__ = (
117121
None,
118122
None,
119123
None,
124+
None,
120125
"<null>",
121126
False,
122127
None,
@@ -264,6 +269,7 @@ def __init__(
264269
self.on_error = c["main"]["on_error"].upper()
265270
self.decimal_format = c["data_formats"]["decimal"]
266271
self.float_format = c["data_formats"]["float"]
272+
self.column_date_formats = c["column_date_formats"]
267273
auth.keyring_initialize(c["main"].as_bool("keyring"), logger=self.logger)
268274
self.show_bottom_toolbar = c["main"].as_bool("show_bottom_toolbar")
269275

@@ -1179,6 +1185,7 @@ def _evaluate_command(self, text):
11791185
table_format=self.table_format,
11801186
dcmlfmt=self.decimal_format,
11811187
floatfmt=self.float_format,
1188+
column_date_formats=self.column_date_formats,
11821189
missingval=self.null_string,
11831190
expanded=expanded,
11841191
max_width=max_width,
@@ -1830,6 +1837,7 @@ def format_status(cur, status):
18301837
"missing_value": settings.missingval,
18311838
"integer_format": settings.dcmlfmt,
18321839
"float_format": settings.floatfmt,
1840+
"column_date_formats": settings.column_date_formats,
18331841
"preprocessors": (format_numbers, format_arrays),
18341842
"disable_numparse": True,
18351843
"preserve_whitespace": True,
@@ -1839,6 +1847,9 @@ def format_status(cur, status):
18391847
if not settings.floatfmt:
18401848
output_kwargs["preprocessors"] = (align_decimals,)
18411849

1850+
if settings.column_date_formats:
1851+
output_kwargs["preprocessors"] += (format_timestamps,)
1852+
18421853
if table_format == "csv":
18431854
# The default CSV dialect is "excel" which is not handling newline values correctly
18441855
# Nevertheless, we want to keep on using "excel" on Windows since it uses '\r\n'

pgcli/pgclirc

+5
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,8 @@ output.null = "#808080"
240240
[data_formats]
241241
decimal = ""
242242
float = ""
243+
244+
# Per column formats for date/timestamp columns
245+
[column_date_formats]
246+
# use strftime format, e.g.
247+
# created = "%Y-%m-%d"

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ dependencies = [
3434
"psycopg-binary >= 3.0.14; sys_platform == 'win32'",
3535
"sqlparse >=0.3.0,<0.6",
3636
"configobj >= 5.0.6",
37-
"cli_helpers[styles] >= 2.2.1",
37+
"cli_helpers[styles] >= 2.4.0",
3838
# setproctitle is used to mask the password when running `ps` in command line.
3939
# But this is not necessary in Windows since the password is never shown in the
4040
# task manager. Also setproctitle is a hard dependency to install in Windows,

tests/test_main.py

+51
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,57 @@ def test_format_output():
7676
assert list(results) == expected
7777

7878

79+
def test_column_date_formats():
80+
settings = OutputSettings(
81+
table_format="psql",
82+
column_date_formats={
83+
"date_col": "%Y-%m-%d",
84+
"datetime_col": "%I:%M:%S %m/%d/%y",
85+
},
86+
)
87+
data = [
88+
("name1", "2024-12-13T18:32:22", "2024-12-13T19:32:22", "2024-12-13T20:32:22"),
89+
("name2", "2025-02-13T02:32:22", "2025-02-13T02:32:22", "2025-02-13T02:32:22"),
90+
]
91+
headers = ["name", "date_col", "datetime_col", "unchanged_col"]
92+
93+
results = format_output("Title", data, headers, "test status", settings)
94+
expected = [
95+
"Title",
96+
"+-------+------------+-------------------+---------------------+",
97+
"| name | date_col | datetime_col | unchanged_col |",
98+
"|-------+------------+-------------------+---------------------|",
99+
"| name1 | 2024-12-13 | 07:32:22 12/13/24 | 2024-12-13T20:32:22 |",
100+
"| name2 | 2025-02-13 | 02:32:22 02/13/25 | 2025-02-13T02:32:22 |",
101+
"+-------+------------+-------------------+---------------------+",
102+
"test status",
103+
]
104+
assert list(results) == expected
105+
106+
107+
def test_no_column_date_formats():
108+
"""Test that not setting any column date formats returns unaltered datetime columns"""
109+
settings = OutputSettings(table_format="psql")
110+
data = [
111+
("name1", "2024-12-13T18:32:22", "2024-12-13T19:32:22", "2024-12-13T20:32:22"),
112+
("name2", "2025-02-13T02:32:22", "2025-02-13T02:32:22", "2025-02-13T02:32:22"),
113+
]
114+
headers = ["name", "date_col", "datetime_col", "unchanged_col"]
115+
116+
results = format_output("Title", data, headers, "test status", settings)
117+
expected = [
118+
"Title",
119+
"+-------+---------------------+---------------------+---------------------+",
120+
"| name | date_col | datetime_col | unchanged_col |",
121+
"|-------+---------------------+---------------------+---------------------|",
122+
"| name1 | 2024-12-13T18:32:22 | 2024-12-13T19:32:22 | 2024-12-13T20:32:22 |",
123+
"| name2 | 2025-02-13T02:32:22 | 2025-02-13T02:32:22 | 2025-02-13T02:32:22 |",
124+
"+-------+---------------------+---------------------+---------------------+",
125+
"test status",
126+
]
127+
assert list(results) == expected
128+
129+
79130
def test_format_output_truncate_on():
80131
settings = OutputSettings(
81132
table_format="psql", dcmlfmt="d", floatfmt="g", max_field_width=10

0 commit comments

Comments
 (0)