Skip to content

feat: word-phrase TimeSpan dehumanize + Humanize round-trip (#691)#1801

Closed
wyf027 wants to merge 9 commits into
Humanizr:mainfrom
wyf027:feat/timespan-dehumanize-word-phrases-691
Closed

feat: word-phrase TimeSpan dehumanize + Humanize round-trip (#691)#1801
wyf027 wants to merge 9 commits into
Humanizr:mainfrom
wyf027:feat/timespan-dehumanize-word-phrases-691

Conversation

@wyf027

@wyf027 wyf027 commented May 26, 2026

Copy link
Copy Markdown

Summary

Extends #1800 (MVP TimeSpan dehumanize) with English word-phrase parsing:

  • Skip , and and separators between number-unit tokens (3 hours, 18 minutes, 3 hours and 18 minutes)
  • Round-trip tests against TimeSpan.Humanize() output for hours/minutes/seconds/weeks

Depends on

Out of scope

  • Localized phrases (fi-FI, etc.)
  • Calendar-month units from Humanize() (11 months, 30 days)
  • toWords: true output (three hours)

Part of #691.

Made with Cursor

wyf027 and others added 5 commits May 26, 2026 21:20
Parse compact duration strings such as 3h18m, 12m 30s, 1000s, 6d, and
colon forms with configurable minutes/seconds interpretation.

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove redundant global using, restore missing colon-format test method,
pin culture with UseCulture, and return false from Try* on overflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use nullable optional default `null` and reflection member order
(ctor, ColonFormat, Default) across all target frameworks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Colon arithmetic mishandles negative components (e.g. -3:18:00). Reject
negative segments so standard TryParse handles them; add regression test.

Co-authored-by: Cursor <cursoragent@cursor.com>
Skip comma and "and" separators between number-unit tokens so strings
like "3 hours, 18 minutes" parse correctly. Add round-trip tests against
TimeSpan.Humanize() output for common unit combinations.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 412704e7-ae39-446e-a4bb-94b99862f411

📥 Commits

Reviewing files that changed from the base of the PR and between 5a5bc22 and 379dc0b.

📒 Files selected for processing (1)
  • src/Humanizer/TimeSpanDehumanizeExtensions.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Humanizer/TimeSpanDehumanizeExtensions.cs

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Parse human-readable duration strings (e.g., "3 hours 18 minutes", "3:18") into TimeSpan objects, supporting compact units, colon-separated formats, and word/phrase inputs.
    • Add configurable interpretation for colon-separated values (controls whether "3:18" is hours:minutes or minutes:seconds) with a sensible default.
  • Tests

    • Added comprehensive tests covering parsing variations, round-trip behavior, and failure/error cases.

Walkthrough

This PR introduces TimeSpan dehumanization—parsing human-friendly duration strings back into TimeSpan values. It adds a new enum (TimeSpanDehumanizeColonFormat), an options class, and extension methods supporting three parsing strategies: compact unit-tokens (e.g., "3h18m"), colon-separated values (e.g., "3:18" with configurable interpretation), and TimeSpan.TryParse fallback.

Changes

TimeSpan Dehumanization Feature

Layer / File(s) Summary
Configuration & Options Types
src/Humanizer/TimeSpanDehumanizeColonFormat.cs, src/Humanizer/TimeSpanDehumanizeOptions.cs
Enum defines two colon-format modes (HoursMinutes vs MinutesSeconds); sealed options class provides Default singleton and init-only ColonFormat property.
Parsing Implementation & Extension Methods
src/Humanizer/TimeSpanDehumanizeExtensions.cs
Public entry points DehumanizeTimeSpan (throws) and TryDehumanizeTimeSpan (returns bool) attempt three parsing strategies in order: unit-token expressions (e.g., "3h18m" with floating-point support), colon-separated values (configurable interpretation of two/three parts), and fallback to TimeSpan.TryParse with invariant culture. Overflow is caught and converted to false/exception.
Public API Surface Approval
tests/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.Approve_Public_Api.*.verified.txt
API surface approved across .NET 4.8, 8.0, 10.0, and 11.0; enum, extension methods, and options class are consistently declared.
Comprehensive Test Suite
tests/Humanizer.Tests/TimeSpanDehumanizeTests.cs
Tests cover successful parsing across all three strategies, round-trip humanize-then-dehumanize consistency, overflow handling, negative values via fallback, and exception/false-return behavior for invalid or null input.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 Hop, skip, and parse!
From seconds to spans so grand,
Humanized time now hops back,
In developer hands it lands. 🕐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding word-phrase TimeSpan dehumanize support and Humanize round-trip tests.
Description check ✅ Passed The description is related to the changeset, detailing the word-phrase parsing feature, round-trip tests, and scope limitations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
src/Humanizer/TimeSpanDehumanizeExtensions.cs (1)

207-211: 💤 Low value

Consider adding "wk" and "wks" abbreviations for weeks.

The week unit supports "w", "week", and "weeks", but common abbreviations "wk" and "wks" are missing. This would improve consistency with other units that support both short and medium abbreviations (e.g., "hr"/"hrs" for hours).

Suggested fix
-        if (UnitEquals(unit, "w") || UnitEquals(unit, "week") || UnitEquals(unit, "weeks"))
+        if (UnitEquals(unit, "w") || UnitEquals(unit, "wk") || UnitEquals(unit, "wks") || UnitEquals(unit, "week") || UnitEquals(unit, "weeks"))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Humanizer/TimeSpanDehumanizeExtensions.cs` around lines 207 - 211, The
UnitEquals checks for weeks in the TimeSpanDehumanizeExtensions method only
match "w", "week", and "weeks"; update the condition that sets result =
TimeSpan.FromDays(7 * amount) (the block using UnitEquals and TimeSpan.FromDays)
to also accept the abbreviations "wk" and "wks" so UnitEquals(unit, "wk") and
UnitEquals(unit, "wks") are included alongside the existing checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/Humanizer/TimeSpanDehumanizeExtensions.cs`:
- Around line 207-211: The UnitEquals checks for weeks in the
TimeSpanDehumanizeExtensions method only match "w", "week", and "weeks"; update
the condition that sets result = TimeSpan.FromDays(7 * amount) (the block using
UnitEquals and TimeSpan.FromDays) to also accept the abbreviations "wk" and
"wks" so UnitEquals(unit, "wk") and UnitEquals(unit, "wks") are included
alongside the existing checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8a506516-b7a8-483b-934a-6a615fa943ed

📥 Commits

Reviewing files that changed from the base of the PR and between f9292aa and cc1f990.

📒 Files selected for processing (8)
  • src/Humanizer/TimeSpanDehumanizeColonFormat.cs
  • src/Humanizer/TimeSpanDehumanizeExtensions.cs
  • src/Humanizer/TimeSpanDehumanizeOptions.cs
  • tests/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.Approve_Public_Api.DotNet10_0.verified.txt
  • tests/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.Approve_Public_Api.DotNet11_0.verified.txt
  • tests/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.Approve_Public_Api.DotNet8_0.verified.txt
  • tests/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.Approve_Public_Api.Net4_8.verified.txt
  • tests/Humanizer.Tests/TimeSpanDehumanizeTests.cs

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc1f99011f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +243 to +246
if (colonFormat == TimeSpanDehumanizeColonFormat.MinutesSeconds
&& TryParseInvariantInt(parts[0], out var minutesOnly)
&& TryParseInvariantDouble(parts[1], out var secondsOnly)
&& minutesOnly >= 0 && secondsOnly >= 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor MinutesSeconds mode for negative two-part values

When ColonFormat is set to MinutesSeconds, negative inputs like "-18:01" do not use that interpretation because this branch rejects any negative component (minutesOnly >= 0 && secondsOnly >= 0), and parsing then falls through to TimeSpan.TryParse, which interprets the same text as hours/minutes. This makes the option inconsistent and can silently return a duration with the wrong magnitude for valid user input.

Useful? React with 👍 / 👎.

@wyf027

wyf027 commented May 26, 2026

Copy link
Copy Markdown
Author

CI all green on cc1f9901

Check Result
Humanizer-CI (Azure #127499) pass
Analyze C# pass
CodeQL / DevSkim pass
CodeRabbit APPROVED

Depends on #1800 — please merge #1800 first, then this adds word-phrase parsing + Humanize() round-trip tests.

Part of #691.

@wyf027

wyf027 commented May 27, 2026

Copy link
Copy Markdown
Author

Stack note: depends on #1800 (MVP parser). Branch includes #1800 commits; after #1800 merges to main, will rebase onto main if needed.

CI green (Azure), CodeRabbit APPROVED, mergeable. Word-phrase parsing + Humanize round-trip tests ready for review.

@wyf027

wyf027 commented Jun 8, 2026

Copy link
Copy Markdown
Author

Addressed the MinutesSeconds negative colon parsing review. The parser now applies the leading sign to the whole colon duration, and -18:01 with ColonFormat = MinutesSeconds has a regression test. Follow-up compile fix is included in 379dc0b; all current PR checks are green.

@wyf027 wyf027 closed this by deleting the head repository Jun 23, 2026
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.

1 participant