Skip to content

Match month and weekday names case-insensitively in from_format - #1068

Open
tlq5l wants to merge 3 commits into
python-pendulum:masterfrom
tlq5l:fix/from-format-case-insensitive-names
Open

tlq5l wants to merge 3 commits into
python-pendulum:masterfrom
tlq5l:fix/from-format-case-insensitive-names

Conversation

@tlq5l

@tlq5l tlq5l commented Oct 21, 2026 •

Copy link
Copy Markdown

Pull Request Check List

  • Added tests for changed code.
  • Updated documentation for changed code. (No docs change needed: the docs never said names were case-sensitive.)

Previously, from_format matched the locale's month and weekday names case-sensitively, so 14nov22 with DDMMMYY raised String does not match format and Turkish MAYIS for Mayıs raised the same error. Cumartesi with dddd in tr parsed as Friday (Cuma), because the parse read the first alternative that matched, not the full match.

This wraps the name tokens (MMMM, MMM, dddd, ddd, dd) in a scoped (?i:...) group in Formatter._replace_tokens and escapes each locale name, so the . in lun. is literal. Locale.match_translation looks a value up by exact match first, then by an escaped re.IGNORECASE full match, the same rule the regex uses, so MAYIS finds May. from_format checks for a full match before it reads the groups, so Cumartesi is Saturday. Other tokens (T, Z, A) keep their case rules. match_translation is only called from from_format parsing.

Callers checked: src/pendulum/__init__.py (from_format is the only caller of Formatter.parse, and the full test suite passes); rust/src/parsing.rs, rust/src/python/parsing.rs, src/pendulum/parser.py and src/pendulum/parsing/__init__.py have their own parsers and do not call it.

Fixes #668.

Ran live on 05c1368: pendulum.from_format('15 MAYIS 2024','DD MMMM YYYY', locale='tr') -> 2024-05-15 00:00:00+00:00 (ValueError before); pendulum.from_format('LUNX','ddd', locale='fr') -> ValueError; pendulum.from_format('14nov22','DDMMMYY') -> 2022-11-14 00:00:00+00:00 (ValueError before).

@Muguett-DBY Muguett-DBY left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried two uppercase Turkish month names and both silently parse as January:

pendulum.from_format("15 MAYIS 2024", "DD MMMM YYYY", locale="tr")
# 2024-01-15 (expected 2024-05-15)

pendulum.from_format("15 NİSAN 2024", "DD MMMM YYYY", locale="tr")
# 2024-01-15 (expected 2024-04-15)

The case-insensitive regex accepts these names, but match_translation() compares the results of .lower(), which differ for Turkish dotted and dotless I. The lookup returns None; _get_parsed_locale_value() then checks value rather than the lookup result, so the missing month defaults to January. On the base branch both inputs raise a format mismatch.

Could we add a Turkish uppercase test and make sure an unmatched translation cannot silently become January?

tlq5l added 2 commits October 28, 2026 14:19
match_translation compared lowercased text, and "MAYIS".lower() does not
equal "Mayıs".lower(). The name regex accepted the text, the lookup returned
None, and from_format silently used January. The lookup now uses the same
re.IGNORECASE rule as the regex, and a name that still cannot be mapped
raises the parser's ValueError instead of defaulting.

The parse also checks a full match and reads its groups directly. Before,
re.sub stopped at the first alternative, so "Cumartesi" parsed as "Cuma".

Agent: claude
Month and weekday names were joined into the parse regex unescaped. In fr,
the name "lun." put its dot in the regex as "any character", so "LUNX"
matched and the lookup then found no name for it. The five name tokens
(MMMM, MMM, dddd, ddd, dd) now escape each locale name before joining.
Other tokens keep their patterns.

The locale sweep test now checks every shipped locale's month and weekday
names in exact, upper and lower case. Each must parse to that exact name.
A ValueError is allowed only where the token regex rejects the text.

Agent: claude
@tlq5l

tlq5l commented Oct 28, 2026

Copy link
Copy Markdown
Author

Thanks for the repro. The new head is 05c1368.

Name matching now uses the same case-insensitive rule as the regex, so MAYIS and NİSAN map to May and April. Locale names are now escaped in the regex, and Cumartesi no longer parses as Cuma. A name the lookup cannot map raises ValueError. A new test covers every shipped locale's names in exact, upper and lower case.

AI disclosure: written and checked with Claude Code (tests and live run agent-run); I take responsibility for it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

String does not match format DDMMMYY

2 participants