Skip to content

[Repo Assist] fix(python): DateTime.TryParse preserves Unspecified kind for naive strings; use fromisoformat in DateTimeOffset.parse#4491

Merged
dbrattli merged 3 commits intomainfrom
repo-assist/fix-issue-3654-datetime-parse-28fe6176e10b696a
Apr 8, 2026
Merged

[Repo Assist] fix(python): DateTime.TryParse preserves Unspecified kind for naive strings; use fromisoformat in DateTimeOffset.parse#4491
dbrattli merged 3 commits intomainfrom
repo-assist/fix-issue-3654-datetime-parse-28fe6176e10b696a

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot commented Apr 7, 2026

🤖 This is an automated pull request from Repo Assist.

Closes #3654

Root Cause

Bug 1 — DateTime.TryParse with naive strings (date.py)

parse() unconditionally called datetime.fromisoformat(string).astimezone(). For a naive string like "2018-10-01T11:12:55" (no timezone component), fromisoformat returns a naive datetime with tzinfo=None. Calling .astimezone() on a naive datetime first assumes the input represents local wall-clock time and then converts it to a timezone-aware datetime in the local timezone. This produced DateTimeKind.Local instead of the correct DateTimeKind.Unspecified.

Bug 2 — DateTimeOffset.parse robustness (date_offset.py)

parse() always delegated to dateutil.parser.parse, even for ISO 8601 strings that Python 3.12+'s built-in datetime.fromisoformat() handles correctly and without extra dependencies.

Fix

date.py — only call .astimezone() when fromisoformat returns a timezone-aware datetime:

parsed = datetime.fromisoformat(string)
if parsed.tzinfo is not None:
    return parsed.astimezone()
return parsed  # Keep naive datetime as-is (DateTimeKind.Unspecified)

date_offset.py — try fromisoformat first, fall back to dateutil for non-ISO formats:

try:
    parsed_dt = datetime.fromisoformat(string)
except ValueError:
    from dateutil import parser
    parsed_dt = parser.parse(string)

This means "2018-07-02T12:23:45+02:00" is now parsed via the stdlib (faster, no timezone ambiguity), while "9/10/2014 1:50:34 PM" still goes through dateutil as before.

Tests Added

  • test DateTime.TryParse preserves Unspecified kind for naive strings — verifies "2018-10-01T11:12:55" gives DateTimeKind.Unspecified and the correct date/time values
  • test DateTimeOffset.TryParse with timezone offset returns correct value — verifies "2018-07-02T12:23:45+02:00" round-trips correctly to DateTimeOffset(2018, 7, 2, 12, 23, 45, 0, TimeSpan.FromHours(2.))

Trade-offs

  • The date_offset.py change does NOT use dateutil for ISO strings, but dateutil is still used as a fallback. No existing tests lose coverage.
  • The date.py fix aligns DateTimeKind semantics with .NET behaviour. Existing tests that only check year/month/day sums are unaffected; tests relying on timezone-aware results for naive strings would need to be fixed (none currently exist).

Note

🔒 Integrity filter blocked 131 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Repo Assist · ● 11.5M ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f

…; use fromisoformat in DateTimeOffset.parse

- Fix date.py parse(): naive datetime strings (no timezone) were incorrectly
  converted to local time via .astimezone(), giving DateTimeKind.Local instead
  of DateTimeKind.Unspecified. Now only call .astimezone() when fromisoformat
  returns an aware datetime.

- Improve date_offset.py parse(): try datetime.fromisoformat() first (Python
  3.12+) before falling back to dateutil, for better reliability with ISO 8601
  strings like '2018-07-02T12:23:45+02:00'. dateutil is still used as a
  fallback for non-standard formats (e.g. '9/10/2014 1:50:34 PM').

- Add test: DateTime.TryParse with naive string returns DateTimeKind.Unspecified
- Add test: DateTimeOffset.TryParse with +02:00 offset returns correct value

Fixes #3654

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions bot added automation Automated changes repo-assist Created by Repo Assist labels Apr 7, 2026
Replace dateutil.parser.parse with datetime.fromisoformat + strptime
fallback for non-ISO formats. fable-library now has zero runtime
dependencies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dbrattli dbrattli marked this pull request as ready for review April 8, 2026 21:12
@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions bot commented Apr 8, 2026

Python Type Checking Results (Pyright)

Metric Value
Total errors 18
Files with errors 4
Excluded files 4
New errors ✅ No
Excluded files with errors (4 files)

These files have known type errors and are excluded from CI. Remove from pyrightconfig.ci.json as errors are fixed.

File Errors Status
temp/tests/Python/test_applicative.py 12 Excluded
temp/tests/Python/test_hash_set.py 3 Excluded
temp/tests/Python/test_nested_and_recursive_pattern.py 2 Excluded
temp/tests/Python/fable_modules/thoth_json_python/encode.py 1 Excluded

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dbrattli dbrattli merged commit 11579ec into main Apr 8, 2026
43 of 46 checks passed
@dbrattli dbrattli deleted the repo-assist/fix-issue-3654-datetime-parse-28fe6176e10b696a branch April 8, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation Automated changes repo-assist Created by Repo Assist

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rework DateTime.Parse/TryParse and DateTimeOffset.Parse/TryParse

1 participant