Skip to content

Preserve time and timezone for regex-extracted dates (#174)#194

Open
apoorvdarshan wants to merge 1 commit into
adbar:masterfrom
apoorvdarshan:fix/issue-174-freetext-timezone
Open

Preserve time and timezone for regex-extracted dates (#174)#194
apoorvdarshan wants to merge 1 commit into
adbar:masterfrom
apoorvdarshan:fix/issue-174-freetext-timezone

Conversation

@apoorvdarshan

Copy link
Copy Markdown

Fixes #174

Root cause

When a date is found in HTML markup (e.g. a <meta property="article:published_time"> tag), the full ISO-8601 string is handed to custom_parse, which uses datetime.fromisoformat/dateutil and keeps the time of day and time zone.

When a date is instead extracted from free text or a JSON-LD block, it goes through regexes. JSON_PUBLISHED, JSON_MODIFIED and TIMESTAMP_PATTERN only captured the YYYY-MM-DD part in group 1, and pattern_search converted just that substring with convert_date(match[1], "%Y-%m-%d", ...). Any trailing Thh:mm:ss±tz was discarded, so find_date returned midnight with no timezone even when the requested outputformat explicitly asked for time/tz.

Reproduction

from htmldate import find_date

jsonld = (
    '<html><head><script type="application/ld+json">'
    '{"datePublished":"2024-11-06T08:37:00+05:30"}'
    "</script></head><body><p>x</p></body></html>"
)
print(find_date(jsonld, outputformat="%Y-%m-%d %H:%M:%S%z", original_date=True))

Observed (on master), time and timezone dropped:

2024-11-06 00:00:00

The same happens for a timestamp sitting in the body text (handled by TIMESTAMP_PATTERN):

freetext = "<html><body><p>Published on 2024-11-06T08:37:00+05:30</p></body></html>"
find_date(freetext, outputformat="%Y-%m-%d %H:%M:%S%z")  # -> '2024-11-06 00:00:00'

After the fix, both return:

2024-11-06 08:37:00+0530

Fix

  • Added a reusable TIME_TZ_RE sub-pattern for the optional ISO-8601 time/timezone suffix and let JSON_PUBLISHED, JSON_MODIFIED and TIMESTAMP_PATTERN capture it as an optional second group.
  • In pattern_search, when that suffix is present and the output format contains a time/timezone directive, the full date + suffix string is parsed through the existing custom_parse (the same routine the HTML-markup path already uses) instead of the date-only substring.

The change is localized to pattern_search and those three patterns. When the output format is date-only (the default %Y-%m-%d), or when no time suffix is present, behaviour is byte-for-byte unchanged — the code falls back to the previous convert_date(match[1], "%Y-%m-%d", ...). The Z suffix is handled via custom_parse's existing dateutil fallback on Python 3.10 where fromisoformat does not accept it.

Tests

Added test_free_text_timezone in tests/unit_tests.py covering the JSON-LD datePublished/dateModified and free-text-body cases with a %Y-%m-%d %H:%M:%S%z format, and asserting the default date-only output is unchanged. The test fails on the unmodified code (2024-11-06 00:00:00) and passes with the fix.

  • pytest tests/unit_tests.py — 25 passed
  • ruff check . — clean; ruff format — clean
  • mypy htmldate/ — no new errors (pre-existing stub-related errors only, count unchanged)

Disclosure: prepared with AI assistance; reviewed and verified locally.

Dates extracted from free text and JSON-LD go through regex patterns
(JSON_PUBLISHED, JSON_MODIFIED, TIMESTAMP_PATTERN) that only captured the
YYYY-MM-DD part, so find_date dropped the time of day and timezone even when
the requested outputformat asked for them (e.g. '%Y-%m-%d %H:%M:%S%z').

Capture the optional ISO-8601 time/timezone suffix in these patterns and, when
the output format contains time or timezone directives, parse the full
timestamp through custom_parse (the same routine the HTML-markup path already
uses). Date-only output is unchanged.
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.

missing timezone

1 participant