Changes for version 0.13 - 2026-07-25
- Bug Fixes
- Fix t/error.t failures when Params::Get >= 0.13 croaks before our own usage check See https://github.com/nigelhorne/DateTime-Format-Genealogy/issues/11
- Fix 'return inside eval' bug in _convert_calendar that silently discarded Hebrew and French Republican calendar conversions
- Fix bare clone (->new() on an object) returning the original reference instead of a copy
- Fix ISO date month 00 silently mapping to December via Perl's negative array-index wrap; month 13-99 no longer produces undef month name
- Fix $@ race condition: three eval blocks (validate_strict, Hebrew calendar, French Republican calendar) now capture $@ into a lexical immediately so a DESTROY firing between the eval and the check cannot silently clear it
- Fix greedy first-group quantifier in 'bet X and Y' / 'from X to Y' range patterns; was picking the last keyword occurrence rather than the first
- Fix unanchored =~ /FRENCH R/ calendar-type test replaced with string equality, preventing false matches on future escape variants
- Fix 3-digit and oversized years returning today's date instead of undef (DateTime::Format::Natural success() was not checked in the GGD-cached path)
- Fix missing optional calendar module returning a Gregorian DateTime instead of undef when DateTime::Calendar::Hebrew or ::FrenchRevolutionary is absent
- Fix undef canonical field in GGD result crashing DateTime::Format::Natural
- Security
- Sanitise user-supplied date strings in all Carp messages via new _safe_str() helper: control characters (including CR/LF for log injection, ANSI escapes) are replaced with '?' and messages are truncated at 120 characters
- Add MAX_CACHE_SIZE guard (10,000 entries) in _date_parser_cached to prevent memory exhaustion when an adversarial caller feeds unbounded unique strings
- Performance
- Cache failed GGD parses as undef in _date_parser_cached; previously only successes were memoised, causing repeated O(n) GGD calls for the same invalid string and duplicate carp emissions
- _date_parser_cached now accepts a plain positional arg instead of routing through Params::Get, removing measurable dispatch overhead from the hot path
- _julian_to_gregorian_offset uses a for-loop with early exit instead of grep, avoiding temporary list allocation on each Julian date conversion
- Enhancements
- Support Object::Configure
- Use Params::Get in new()
- Use Params::Validate::Strict to reject unknown parameter keys
- Use Sub::Private (enforce mode) for _convert_calendar and _julian_to_gregorian_offset; use Sub::Protected for _date_parser_cached
- Use Readonly constants for month alias table, Julian offset tiers, and new MAX_CACHE_SIZE limit
- Consolidate French/German month aliases (Janv, Juli, Mai) into the main %MONTH_ALIAS lookup table; remove separate special-case branches
- quiet and strict flags now fall back to object-level attributes when not supplied per-call to parse_datetime
- Full POD added: EXAMPLE, API SPECIFICATION, MESSAGES, FORMAL SPECIFICATION, PSEUDOCODE, and LIMITATIONS sections
- New t/locales.t covering POSIX locale behaviour
- New t/unit.t, t/integration.t, t/edge_cases.t, t/extended_tests.t, t/function.t — comprehensive black-box, integration, hostile-input, coverage-gap, and white-box test suites
- Eliminate duplicate =head1 SEE ALSO section
- Minimum Perl version corrected from 5.006_001 to 5.010 (// and //= require it)
- use Carp () and use Scalar::Util () — explicit non-import; all calls use fully-qualified names throughout
- t/edge_cases.t: timing ceiling raised to 30 s and alarm() subtest skips on Windows where alarm() is not implemented
Documentation
Modules
Create a DateTime object from a genealogy date string