Skip to content

fix(cli): reject a --since date later than --until - #1731

Closed
ravsau wants to merge 1 commit into
ccusage:mainfrom
ravsau:fix/reject-reversed-date-range
Closed

ravsau wants to merge 1 commit into
ccusage:mainfrom
ravsau:fix/reject-reversed-date-range

Conversation

@ravsau

@ravsau ravsau commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1729.

A window whose --since date is later than its --until date previously loaded every entry, kept nothing, and printed an empty report with exit 0, which reads as "no usage data" rather than a typo in the flags.

Both bounds are already normalized to YYYYMMDD before storage, so the new date_window_error check in ccusage-cli-parser is a string comparison. It runs after last_option_error, once config bounds and flags are merged, so it covers flags, config-file bounds, and a mix of both, for every report command. The per-command shared-args match that last_option_error used is now a shared report_shared helper so both checks read the same effective options.

Error and exit code follow the existing date-bound rejection from #1604:

The --since date '20260914' is later than the --until date '20260901'.
Run 'ccusage --help' for usage.

Tests cover bare, unified, session, claude session, blocks, and codex monthly invocations, an equal-date window, a config since with a flag until, and a real config file whose reversed window is rejected and then repaired by a flag. The CLI options guide gets one sentence.

Validation: cargo test -p ccusage-cli-parser (85 passed), cargo clippy --all-targets -D warnings, cargo fmt --check, and the release binary end to end.


Summary by cubic

Fixes #1729 by rejecting a date window whose --since date is later than its --until date. Previously such a window loaded every entry, kept nothing, and printed an empty report with exit 0, which read as missing data rather than a typo.

  • The check runs after config bounds and flags are merged, so it covers flags, config-file bounds, and a mix of both for every report command.
  • Both bounds are already normalized to YYYYMMDD, so the validation is a simple string comparison.
  • The error message and non-zero exit code match the existing date-bound rejection.

Written for commit fb0409f. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Invalid date ranges are now rejected when the --since date is later than the --until date.
    • Validation applies consistently across command forms and combinations of command-line and configuration-file options.
    • Equal start and end dates remain accepted.
    • Clear error messages and non-zero exit codes are provided for reversed date ranges.
  • Documentation

    • Updated CLI option documentation to describe date-range validation behavior.

A reversed window loaded every entry, kept nothing, and printed an empty
report with exit 0, which reads as "no usage data" rather than a typo.
Both bounds are already normalized to YYYYMMDD, so the check is a string
comparison that runs after config bounds and flags are merged, for every
report command.
@coderabbitai

coderabbitai Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: f7492695-c7cc-410c-8660-d1a9cb6c2413

📥 Commits

Reviewing files that changed from the base of the PR and between 0831c52 and fb0409f.

📒 Files selected for processing (3)
  • docs/guide/cli-options.md
  • rust/crates/ccusage-cli-parser/src/parser.rs
  • rust/crates/ccusage-cli-parser/src/tests.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The CLI parser now rejects reversed --since and --until dates after merging command-line and configuration values. Tests cover supported command forms, equal dates, and mixed configuration and flag inputs. The CLI options guide documents the validation.

Changes

Date window validation

Layer / File(s) Summary
Parser date-window validation
rust/crates/ccusage-cli-parser/src/parser.rs, docs/guide/cli-options.md
The parser shares command argument selection between --last validation and date-window validation. It rejects normalized since dates later than until dates and documents the resulting non-zero exit.
Date-window test coverage
rust/crates/ccusage-cli-parser/src/tests.rs
Tests cover reversed and equal dates across root and command forms, plus configuration-only and mixed configuration and flag values.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: ryoppippi

Merge Risk: ⚪ Minimal · up to fb040

Reversed date windows are consistently rejected across supported report commands and merged configuration inputs.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 2 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: rejecting a --since date that is later than --until.
Linked Issues check ✅ Passed The changes satisfy #1729. parse_from_with_config merges configuration and flags, then checks the normalized since and until bounds before the CLI can load usage data. The check covers root repo…
Out of Scope Changes check ✅ Passed The changes stay within #1729. The parser change implements date-window validation. The tests cover the required command and configuration combinations. The documentation update describes the new vali…
Full details: Docstring Coverage

Explanation

Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 2 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ 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.

@github-actions

Copy link
Copy Markdown
Contributor

Pullfrog could not complete the automated PR review. The PR was left open for maintainer review.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="rust/crates/ccusage-cli-parser/src/parser.rs">

<violation number="1" location="rust/crates/ccusage-cli-parser/src/parser.rs:1112">
P3: The new date-window check also fires for `ccusage statusline`, even though statusline never uses `--since`/`--until`. `report_shared` falls back to `root_shared` for `Command::Statusline`, so a config file with a reversed `defaults.since`/`defaults.until` now makes `ccusage statusline` fail to parse and exit non-zero, while `run_statusline`/`statusline_shared` in `rust/crates/ccusage/src/commands/mod.rs` never read those bounds (they build a fresh `SharedArgs` without `since`/`until`). The PR scope is "every report command", and the statusline fallback is only needed by `last_option_error`; consider skipping the date-window check for statusline so a stale reversed default window doesn't break a command that ignores it.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

/// orders them. A reversed window would otherwise load everything and print an
/// empty report that looks like missing data.
fn date_window_error(command: Option<&Command>, root_shared: &SharedArgs) -> Option<String> {
let (shared, _) = report_shared(command, root_shared);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: The new date-window check also fires for ccusage statusline, even though statusline never uses --since/--until. report_shared falls back to root_shared for Command::Statusline, so a config file with a reversed defaults.since/defaults.until now makes ccusage statusline fail to parse and exit non-zero, while run_statusline/statusline_shared in rust/crates/ccusage/src/commands/mod.rs never read those bounds (they build a fresh SharedArgs without since/until). The PR scope is "every report command", and the statusline fallback is only needed by last_option_error; consider skipping the date-window check for statusline so a stale reversed default window doesn't break a command that ignores it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage-cli-parser/src/parser.rs, line 1112:

<comment>The new date-window check also fires for `ccusage statusline`, even though statusline never uses `--since`/`--until`. `report_shared` falls back to `root_shared` for `Command::Statusline`, so a config file with a reversed `defaults.since`/`defaults.until` now makes `ccusage statusline` fail to parse and exit non-zero, while `run_statusline`/`statusline_shared` in `rust/crates/ccusage/src/commands/mod.rs` never read those bounds (they build a fresh `SharedArgs` without `since`/`until`). The PR scope is "every report command", and the statusline fallback is only needed by `last_option_error`; consider skipping the date-window check for statusline so a stale reversed default window doesn't break a command that ignores it.</comment>

<file context>
@@ -1092,6 +1105,17 @@ fn last_option_error(command: Option<&Command>, root_shared: &SharedArgs) -> Opt
+/// orders them. A reversed window would otherwise load everything and print an
+/// empty report that looks like missing data.
+fn date_window_error(command: Option<&Command>, root_shared: &SharedArgs) -> Option<String> {
+    let (shared, _) = report_shared(command, root_shared);
+    let since = shared.since.as_deref()?;
+    let until = shared.until.as_deref()?;
</file context>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one minor observation inline, otherwise mergeable.

Reviewed changes
Reviewed fb0409f end to end via the raw diff (3 files).

  • Reversed window rejection — new date_window_error in ccusage-cli-parser rejects --since later than --until with exit 2 and a usage hint, after last_option_error so flag/config mixes are covered.
  • Shared resolver refactor — per-command shared-args match extracted into report_shared reused by both checks, no behavior change to --last validation.
  • Tests and docs — exact-error cases across bare, unified, session, claude session, blocks, and codex monthly, plus equal-date acceptance and config/flag merge cases; one sentence added to the CLI options guide.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using Muse Spark Contributor (free via Pullfrog for OSS) | 𝕏

let since = shared.since.as_deref()?;
let until = shared.until.as_deref()?;
(since > until)
.then(|| format!("The --since date '{since}' is later than the --until date '{until}'."))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shows the normalized YYYYMMDD form even when the user typed YYYY-MM-DD. Still recognizable, so not blocking — only note it if you want the message to echo the original spelling like the invalid-date error does.

@pkg-pr-new

pkg-pr-new Bot commented Sep 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

ccusage

npx https://pkg.pr.new/ccusage@1731

@ccusage/ccusage-darwin-arm64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-arm64@1731

@ccusage/ccusage-darwin-x64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-x64@1731

@ccusage/ccusage-linux-arm64

npx https://pkg.pr.new/@ccusage/ccusage-linux-arm64@1731

@ccusage/ccusage-linux-x64

npx https://pkg.pr.new/@ccusage/ccusage-linux-x64@1731

@ccusage/ccusage-win32-x64

npx https://pkg.pr.new/@ccusage/ccusage-win32-x64@1731

commit: fb0409f

@ryoppippi

Copy link
Copy Markdown
Member

Thank you for the fix. Maintainer review found one statusline regression: that command ignores report date bounds, but reversed configured defaults made it fail. I carried your original commit into #1733 with authorship preserved, added the regression fix and test, and ran the full local gates. Closing this copy to avoid duplicate CI and review; #1733 is the merge candidate.

@ryoppippi ryoppippi closed this Sep 16, 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.

A --since date later than --until is accepted and prints an empty report

2 participants