How Outfancy Detects and Parses Date/Time Formats in Table Columns

Outfancy uses a two-step pipeline that first normalizes and pattern-matches individual cells using datetime.strptime in widgets.py, then applies a configurable majority-vote threshold (defaulting to 70%) in table.py to classify entire columns as dates or times.

Outfancy is a Python library designed for rendering tables in terminal environments with intelligent type inference. When processing tabular data, the library must accurately distinguish between temporal data and other types to apply appropriate formatting. According to the carlosplanchon/outfancy source code, this detection relies on a hybrid approach combining string normalization, multi-pattern parsing, and statistical aggregation.

The Two-Step Type Detection Pipeline

Outfancy determines column types through a hierarchical process that moves from individual cell inspection to column-level analysis.

Step 1: Normalization and Pattern Matching in outfancy/widgets.py

The first layer of detection occurs in outfancy/widgets.py, where helper functions inspect raw string values. The is_date() function serves as the primary entry point for date detection, while is_complete_hour() handles time values.

Before attempting to parse a string, Outfancy calls normalise_date() to standardize separators. This preprocessing replaces common delimiters—including slashes (/), colons (:), periods (.), and at symbols (@)—with hyphens (-). This normalization allows the parser to handle varied input formats like 12/08/2023 or 12.08.2023 uniformly.

After normalization, is_date() attempts to parse the string against multiple datetime.strptime patterns covering European formats with both two-digit and four-digit years:


# From outfancy/widgets.py – date detection logic (lines 83-94)

def is_date(text: str) -> bool:
    text = normalise_date(text)  # Normalizes separators to hyphens

    for fmt in ["%d-%m-%Y", "%d-%m-%y",
                "%d-%m-%Y %H-%M-%S", "%d-%m-%y %H-%M-%S"]:
        try:
            strptime(text, fmt)
            return True
        except ValueError:
            pass
    return False

For time detection, is_complete_hour() employs similar pattern-matching logic to identify valid hour formats before the column-level aggregation phase.

Step 2: Majority Voting in outfancy/table.py

After individual cells are classified, outfancy/table.py implements a majority-vote algorithm to determine definitive column types. The Table class iterates through each row, calling the widget helpers (lines 38-44), and accumulates provisional type labels including 'date', 'time', 'id', and 'value'.

If the percentage of cells classified as dates exceeds the threshold stored in self.chk_dtli_date (defaulting to 70%), the entire column is labeled as a date column. The same statistical approach applies to time columns using self.chk_dtli_time (lines 103-105):


# From outfancy/table.py – column classification logic

elif widgets.is_complete_hour(field):
    the_type = 'time'
elif widgets.is_date(field):
    the_type = 'date'

# Aggregation phase determines final type:

if date_type >= self.chk_dtli_date:
    the_type = 'date'
elif time_type >= self.chk_dtli_time:
    the_type = 'time'

Configurable Detection Thresholds

The default 70% threshold prevents misclassification when columns contain occasional date strings among predominantly non-date data. You can adjust these sensitivity levels by modifying the attributes on your Table instance before calling analyze(). Lowering chk_dtli_date makes the detection more permissive, while raising it requires stronger consensus across the column.

Practical Usage Examples

Detecting Individual Date Strings

You can use the widget functions directly to validate single values without processing an entire table:

from outfancy import widgets

s = "12/08/2023 14-30-00"
if widgets.is_date(s):
    print("Detected a date!")
else:
    print("Not a date.")

Automatic Column Detection in Tables

When processing tabular data, the Table class handles detection automatically during the analysis phase:

from outfancy import Table

# Assume data is a list of lists (e.g., from a CSV)

tbl = Table(data)
tbl.analyze()  # Runs the full detection pipeline

print(tbl.column_types)  # Output: ['id', 'name', 'date', 'value']

Summary

  • Outfancy detects dates through a two-step pipeline: cell-level pattern matching followed by column-level threshold analysis.
  • The is_date() function in outfancy/widgets.py normalizes separators and attempts multiple strptime patterns to identify valid dates.
  • Column classification occurs in outfancy/table.py using a majority-vote system with configurable thresholds.
  • The system supports European date formats (day-month-year) with optional time components and handles various input separators through automatic normalization.

Frequently Asked Questions

What specific date formats does Outfancy support?

Outfancy primarily supports European date formats (day-month-year) including "%d-%m-%Y", "%d-%m-%y", and their variants with time components ("%d-%m-%Y %H-%M-%S"). The normalise_date() function in widgets.py preprocesses strings to handle various separators like slashes, colons, and periods before the pattern matching occurs, effectively supporting inputs like 12/08/2023 or 12.08.2023 interchangeably.

How can I adjust the detection sensitivity for date columns?

You can modify the chk_dtli_date attribute on your Table instance, which represents the percentage of cells that must match date patterns before the column is classified accordingly. The default value is 70 (meaning 70%), but you can raise this for stricter detection or lower it for more permissive classification when dealing with noisy datasets.

What happens if a column contains mixed dates and text values?

If fewer than 70% of cells (or your configured threshold) match the date or time patterns, Outfancy will not classify the column as temporal data. Instead, the column will likely receive a label such as 'value' or 'id' based on the majority content type, preventing date-specific formatting from being applied to mixed-type columns.

How does Outfancy distinguish time-only columns from datetime columns?

Time-only columns are processed using the is_complete_hour() function in widgets.py, which checks for valid hour formats separate from date detection. Similar to the date logic, the system requires a configurable percentage of cells (controlled by self.chk_dtli_time) to match time patterns before classifying the column as 'time' in table.py, allowing the renderer to apply time-specific formatting distinct from full datetime values.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →