Skip to content

Commit 14e1fe2

Browse files
committed
Adding format_timestamps preprocessor
1 parent 3e91c81 commit 14e1fe2

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This project receives help from these awesome contributors:
2525
- Mel Dafert
2626
- Andrii Kohut
2727
- Roland Walker
28+
- Doug Harris
2829

2930
Thanks
3031
------

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
TBD
4+
---
5+
6+
- Added format_timestamps preprocessor for per-column date/time formatting.
7+
38
## Version 2.3.1
49

510
- Don't escape newlines in `ascii` tables, and add `ascii_escaped` table format.

cli_helpers/tabular_output/preprocessors.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"""These preprocessor functions are used to process data prior to output."""
33

44
import string
5+
from datetime import datetime
56

67
from cli_helpers import utils
78
from cli_helpers.compat import text_type, int_types, float_types, HAS_PYGMENTS, Token
@@ -353,3 +354,44 @@ def _format_number(field, column_type):
353354
[_format_number(v, column_types[i]) for i, v in enumerate(row)] for row in data
354355
)
355356
return data, headers
357+
358+
359+
def format_timestamps(data, headers, column_date_formats=None, **_):
360+
"""Format timestamps according to user preference.
361+
362+
This allows for per-column formatting for date, time, or datetime like data.
363+
364+
Add a `column_date_formats` section to your config file with separate lines for each column
365+
that you'd like to specify a format using `name=format`. Use standard Python strftime
366+
formatting strings
367+
368+
Example: `signup_date = "%Y-%m-%d"`
369+
370+
:param iterable data: An :term:`iterable` (e.g. list) of rows.
371+
:param iterable headers: The column headers.
372+
:param str column_date_format: The format strings to use for specific columns.
373+
:return: The processed data and headers.
374+
:rtype: tuple
375+
376+
"""
377+
if column_date_formats is None:
378+
return iter(data), headers
379+
380+
def _format_timestamp(value, name, column_date_formats):
381+
if name not in column_date_formats:
382+
return value
383+
try:
384+
dt = datetime.fromisoformat(value)
385+
return dt.strftime(column_date_formats[name])
386+
except (ValueError, TypeError):
387+
# not a date
388+
return value
389+
390+
data = (
391+
[
392+
_format_timestamp(v, headers[i], column_date_formats)
393+
for i, v in enumerate(row)
394+
]
395+
for row in data
396+
)
397+
return data, headers

tests/tabular_output/test_preprocessors.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
override_tab_value,
1717
style_output,
1818
format_numbers,
19+
format_timestamps,
1920
)
2021

2122
if HAS_PYGMENTS:
@@ -348,3 +349,25 @@ def test_enforce_iterable():
348349
assert False, "{} doesn't return iterable".format(name)
349350
if isinstance(preprocessed[1], types.GeneratorType):
350351
assert False, "{} returns headers as iterator".format(name)
352+
353+
354+
def test_format_timestamps():
355+
data = (
356+
("name1", "2024-12-13T18:32:22", "2024-12-13T19:32:22", "2024-12-13T20:32:22"),
357+
("name2", "2025-02-13T02:32:22", "2025-02-13T02:32:22", "2025-02-13T02:32:22"),
358+
("name3", None, "not-actually-timestamp", "2025-02-13T02:32:22"),
359+
)
360+
headers = ["name", "date_col", "datetime_col", "unchanged_col"]
361+
column_date_formats = {
362+
"date_col": "%Y-%m-%d",
363+
"datetime_col": "%I:%M:%S %m/%d/%y",
364+
}
365+
result_data, result_headers = format_timestamps(data, headers, column_date_formats)
366+
367+
expected = [
368+
["name1", "2024-12-13", "07:32:22 12/13/24", "2024-12-13T20:32:22"],
369+
["name2", "2025-02-13", "02:32:22 02/13/25", "2025-02-13T02:32:22"],
370+
["name3", None, "not-actually-timestamp", "2025-02-13T02:32:22"],
371+
]
372+
assert expected == list(result_data)
373+
assert headers == result_headers

0 commit comments

Comments
 (0)