Skip to content

Commit 881c264

Browse files
fix: Default to day-first format for invalid/unknown locales (#1168)
## Summary Fixes a bug where invalid/unknown locales would default to month-first (American) format instead of day-first format. Since most of the world uses day-first, this is the better default. ## Problem When an invalid or unknown locale was provided (e.g., typo, unsupported locale), the exception handler would return `False` (month-first): ```python except Exception: return False # ❌ Defaults to month-first (MM/DD/YYYY) ``` This caused incorrect parsing: - User provides `locale="xyz_ABC"` (typo) - Date `"01/02/2023"` gets parsed as **January 2nd** (month-first) - Should be parsed as **February 1st** (day-first, more common globally) ### Why this is wrong Only a handful of locales use month-first format: - `en_US` - American English - A few Pacific island locales **Most of the world uses day-first:** - All of Europe - Most of Asia - Middle East - Africa - South America - Australia ## Solution Changed exception handler to return `True` (day-first) for invalid locales: ```python except Exception: # Invalid/unknown locale: default to day-first since most of the world uses it # Only en_US and a few other locales use month-first (MM/DD/YYYY) return True # ✅ Defaults to day-first (DD/MM/YYYY) ``` ### The logic now: 1. **ISO 8601** (`YYYY-MM-DD`) → `False` (month before day) 2. **No locale** (`None`) → `False` (backward compat, American default) 3. **Valid locale** → Check Babel CLDR data 4. **Invalid locale** → `True` (day-first is global default) ## Code Changes **Before:** ```python except Exception: return False # Defaulted to month-first ``` **After:** ```python except Exception: # Invalid/unknown locale: default to day-first since most of the world uses it # Only en_US and a few other locales use month-first (MM/DD/YYYY) return True # Default to day-first (DD/MM/YYYY) ``` ## Testing **Updated test:** ```python def test_invalid_locale_returns_true(self) -> None: # Invalid/unknown locales default to day-first since most of world uses it assert _should_use_day_first("01/15/2023", "invalid_LOCALE") is True assert _should_use_day_first("15/01/2023", "xyz_ABC") is True ``` **New integration test:** ```python def test_invalid_locale_defaults_to_day_first(self) -> None: parser = TimestampParser() # With an invalid locale, ambiguous dates should parse as day-first with set_locale_context("invalid_LOCALE"): result = parser.parse("01/02/2023") assert "2023-02-01" in result # February 1st (day-first) ``` - ✅ All 19 timestamp parser tests pass - ✅ All 547 tests pass - ✅ Linting clean ## Impact - **Fixes bug** - Invalid locales now use the globally more common format - **Better user experience** - Fewer parsing errors for international users - **No breaking changes** - Only affects invalid locale case (which was already broken) - **Backward compatible** - `None` locale still defaults to American format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.1 <noreply@anthropic.com>
1 parent a8c5ae1 commit 881c264

2 files changed

Lines changed: 24 additions & 5 deletions

File tree

src/allotropy/parsers/utils/timestamp_parser.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def _should_use_day_first(time_str: str, locale_str: str | None) -> bool:
4242
if re.match(r"^\d{4}[-/.]", time_str):
4343
return False
4444

45-
# Use locale data for non-ISO formats
45+
# No locale specified: default to American format for backward compatibility
4646
if not locale_str:
4747
return False
4848

@@ -60,7 +60,9 @@ def _should_use_day_first(time_str: str, locale_str: str | None) -> bool:
6060

6161
return False
6262
except Exception:
63-
return False
63+
# Invalid/unknown locale: default to day-first since most of the world uses it
64+
# Only en_US and a few other locales use month-first (MM/DD/YYYY)
65+
return True
6466

6567

6668
class TimestampParser:

tests/parsers/utils/timestamp_parser_test.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ def test_none_locale_returns_false(self) -> None:
4242
assert _should_use_day_first("01/15/2023", None) is False
4343
assert _should_use_day_first("15/01/2023", None) is False
4444

45-
def test_invalid_locale_returns_false(self) -> None:
46-
# Invalid locales default to False
47-
assert _should_use_day_first("01/15/2023", "invalid_LOCALE") is False
45+
def test_invalid_locale_returns_true(self) -> None:
46+
# Invalid/unknown locales default to day-first since most of world uses it
47+
assert _should_use_day_first("01/15/2023", "invalid_LOCALE") is True
48+
assert _should_use_day_first("15/01/2023", "xyz_ABC") is True
49+
assert _should_use_day_first("01/02/2023", "not_a_real_locale") is True
4850

4951

5052
class TestTimestampParser:
@@ -142,6 +144,21 @@ def test_parse_invalid_date_raises_error(self) -> None:
142144
):
143145
parser.parse("not a date")
144146

147+
def test_invalid_locale_defaults_to_day_first(self) -> None:
148+
"""Invalid/unknown locales should default to day-first (most common globally)."""
149+
parser = TimestampParser()
150+
151+
# With an invalid locale, ambiguous dates should parse as day-first
152+
# 01/02/2023 should be February 1st (day-first), not January 2nd
153+
with set_locale_context("invalid_LOCALE"):
154+
result = parser.parse("01/02/2023")
155+
assert "2023-02-01" in result, "Invalid locale should default to day-first"
156+
157+
# Another invalid locale - should still use day-first
158+
with set_locale_context("xyz_ABC"):
159+
result = parser.parse("15/01/2023")
160+
assert "2023-01-15" in result, "Unknown locale should default to day-first"
161+
145162
def test_real_world_examples(self) -> None:
146163
"""Test real-world date formats from various instruments."""
147164
parser = TimestampParser()

0 commit comments

Comments
 (0)