Skip to content

Conversation

@travisjhicks
Copy link
Contributor

@travisjhicks travisjhicks commented Aug 6, 2025

Suggested fix for issue #433.

formatDateCompact is being given a date in YYYY-MM-DD format that has already been converted to the local time zone. Constructing a new Date from that string interprets it as UTC, which causes an off-by-one error for users at a negative offset from UTC.

Summary by CodeRabbit

  • Bug Fixes
    • Improved date formatting to ensure dates in "YYYY-MM-DD" format are displayed correctly according to local time, preventing timezone-related discrepancies in the formatted output.

@coderabbitai
Copy link

coderabbitai bot commented Aug 6, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The internal logic of the formatDateCompact function in src/data-loader.ts was updated to better handle "YYYY-MM-DD" date strings. The function now uses schema validation to detect this format and appends "T00:00:00" or "T00:00:00Z" depending on the presence of a timezone, ensuring dates are interpreted correctly as local or UTC midnight. No public API or exported signature was changed.

Changes

Cohort / File(s) Change Summary
Date Parsing Logic Update
src/data-loader.ts
Modified formatDateCompact to detect "YYYY-MM-DD" strings via schema validation and append "T00:00:00" or "T00:00:00Z" for local or UTC midnight parsing, preventing unintended UTC interpretation. Added tests to verify correct behavior with and without timezone.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant formatDateCompact
    participant SchemaValidator
    participant DateParser

    Caller->>formatDateCompact: Call with date string and optional timezone
    formatDateCompact->>SchemaValidator: Validate if "YYYY-MM-DD" format
    alt Matches "YYYY-MM-DD"
        formatDateCompact->>DateParser: Append "T00:00:00" or "T00:00:00Z" based on timezone, then parse
    else Not matched
        formatDateCompact->>DateParser: Parse date string as is
    end
    formatDateCompact-->>Caller: Return formatted compact date string
Loading

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

Suggested reviewers

  • ryoppippi

Poem

A hop and a skip through the fields of time,
Dates parsed at midnight, now local—sublime!
No more UTC confusion in the carrot patch,
Just compact dates, a perfect match.
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @travisjhicks, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've addressed an off-by-one error in the formatDateCompact function that occurred when processing YYYY-MM-DD formatted dates. The issue stemmed from JavaScript's Date constructor interpreting these strings as UTC, which caused incorrect date calculations for users in time zones with a negative offset from UTC. My change ensures these dates are consistently parsed as local time, resolving the discrepancy.

Highlights

  • Date Parsing Fix: Modified formatDateCompact in src/data-loader.ts to correctly parse YYYY-MM-DD date strings. Previously, new Date() would interpret these as UTC, leading to an off-by-one day error for users in negative UTC timezones. The fix involves appending T00:00:00 to such date strings before creating a Date object, ensuring they are parsed as local time.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly fixes an off-by-one error in date formatting by ensuring that YYYY-MM-DD date strings are parsed in the local timezone. The change is well-implemented and includes helpful comments. I have one suggestion to improve maintainability by reusing an existing schema for date validation, which will make the code more robust and easier to maintain.

Comment on lines 572 to 574
const localDateStr = /^\d{4}-\d{2}-\d{2}$/.test(dateStr)
? `${dateStr}T00:00:00`
: dateStr;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

To improve maintainability and avoid duplicating the date-matching logic, you can reuse dailyDateSchema which is already imported in this file. This ensures that if the date format definition changes in the future, it only needs to be updated in one place.

	const localDateStr = dailyDateSchema.safeParse(dateStr).success
		? `${dateStr}T00:00:00`
		: dateStr;

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0dff20 and af1f389.

📒 Files selected for processing (1)
  • src/data-loader.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{js,jsx,ts,tsx}: Lint code using ESLint MCP server (available via Claude Code tools)
Format code with ESLint (writes changes) using bun run format
No console.log allowed except where explicitly disabled with eslint-disable
Do not use console.log. Use logger.ts instead.

Files:

  • src/data-loader.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Type check with TypeScript using bun typecheck

Files:

  • src/data-loader.ts
**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.ts: File paths always use Node.js path utilities for cross-platform compatibility
Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
Use Result.try() for wrapping operations that may throw (JSON parsing, etc.)
Use Result.isFailure() for checking errors (more readable than !Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators
For async operations: create wrapper function with Result.try() then call it
Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
Always use Result.isFailure() and Result.isSuccess() type guards for better code clarity
Variables: start with lowercase (camelCase) - e.g., usageDataSchema, modelBreakdownSchema
Types: start with uppercase (PascalCase) - e.g., UsageData, ModelBreakdown
Constants: can use UPPER_SNAKE_CASE - e.g., DEFAULT_CLAUDE_CODE_PATH
Only export constants, functions, and types that are actually used by other modules
Internal/private constants that are only used within the same file should NOT be exported
Always check if a constant is used elsewhere before making it export const vs just const
All test files must use current Claude 4 models, not outdated Claude 3 models
Test coverage should include both Sonnet and Opus models for comprehensive validation
Model names in tests must exactly match LiteLLM's pricing database entries
When adding new model tests, verify the model exists in LiteLLM before implementation
Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues
Dynamic imports using await import() should only be used within test blocks to avoid tree-shaking issues
Mock data is created using fs-fixture with createFixture() for Claude data directory simulation
In-source testing pattern: Tests are written...

Files:

  • src/data-loader.ts
🧠 Learnings (1)
📚 Learning: applies to **/*.ts : mock data is created using `fs-fixture` with `createfixture()` for claude data ...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Mock data is created using `fs-fixture` with `createFixture()` for Claude data directory simulation

Applied to files:

  • src/data-loader.ts

- Use dailyDateSchema parsing instead of regex
- If time zone is specified, interpret date as UTC
- Add unit tests
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af1f389 and beb86f5.

📒 Files selected for processing (1)
  • src/data-loader.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{js,jsx,ts,tsx}: Lint code using ESLint MCP server (available via Claude Code tools)
Format code with ESLint (writes changes) using bun run format
No console.log allowed except where explicitly disabled with eslint-disable
Do not use console.log. Use logger.ts instead.

Files:

  • src/data-loader.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Type check with TypeScript using bun typecheck

Files:

  • src/data-loader.ts
**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.ts: File paths always use Node.js path utilities for cross-platform compatibility
Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
Use Result.try() for wrapping operations that may throw (JSON parsing, etc.)
Use Result.isFailure() for checking errors (more readable than !Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators
For async operations: create wrapper function with Result.try() then call it
Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
Always use Result.isFailure() and Result.isSuccess() type guards for better code clarity
Variables: start with lowercase (camelCase) - e.g., usageDataSchema, modelBreakdownSchema
Types: start with uppercase (PascalCase) - e.g., UsageData, ModelBreakdown
Constants: can use UPPER_SNAKE_CASE - e.g., DEFAULT_CLAUDE_CODE_PATH
Only export constants, functions, and types that are actually used by other modules
Internal/private constants that are only used within the same file should NOT be exported
Always check if a constant is used elsewhere before making it export const vs just const
All test files must use current Claude 4 models, not outdated Claude 3 models
Test coverage should include both Sonnet and Opus models for comprehensive validation
Model names in tests must exactly match LiteLLM's pricing database entries
When adding new model tests, verify the model exists in LiteLLM before implementation
Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues
Dynamic imports using await import() should only be used within test blocks to avoid tree-shaking issues
Mock data is created using fs-fixture with createFixture() for Claude data directory simulation
In-source testing pattern: Tests are written...

Files:

  • src/data-loader.ts
🧠 Learnings (3)
📚 Learning: applies to **/*.ts : mock data is created using `fs-fixture` with `createfixture()` for claude data ...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Mock data is created using `fs-fixture` with `createFixture()` for Claude data directory simulation

Applied to files:

  • src/data-loader.ts
📚 Learning: applies to **/*.ts : variables: start with lowercase (camelcase) - e.g., `usagedataschema`, `modelbr...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Variables: start with lowercase (camelCase) - e.g., `usageDataSchema`, `modelBreakdownSchema`

Applied to files:

  • src/data-loader.ts
📚 Learning: applies to **/*.ts : tests depend on real pricing data from litellm - failures may indicate model av...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues

Applied to files:

  • src/data-loader.ts
🪛 ESLint
src/data-loader.ts

[error] 572-572: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 572-572: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 572-572: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 572-572: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 573-573: Unexpected nullable string value in conditional. Please handle the nullish/empty cases explicitly.

(ts/strict-boolean-expressions)


[error] 1443-1443: Trailing spaces not allowed.

(style/no-trailing-spaces)


[error] 1445-1445: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1445-1445: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1445-1445: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 1446-1446: Trailing spaces not allowed.

(style/no-trailing-spaces)


[error] 1448-1448: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1448-1448: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1448-1448: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)

Comment on lines 570 to 576
// For YYYY-MM-DD format, append T00:00:00 to parse as local date
// Without this, new Date('YYYY-MM-DD') interprets as UTC midnight
const date = dailyDateSchema.safeParse(dateStr).success
? timezone
? new Date(`${dateStr}T00:00:00Z`)
: new Date(`${dateStr}T00:00:00`)
: new Date(dateStr);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix implemented correctly but consider timezone edge cases

The implementation successfully addresses the off-by-one error by using schema validation and appropriate time suffixes. However, there are a few concerns:

  1. Type safety issues: The ESLint errors indicate unsafe operations on the safeParse result
  2. Timezone edge case: Per previous review feedback, this approach may still cause day shifts when the host timezone differs from the requested timezone for formatting

Consider the more robust approach suggested in previous reviews:

-	const date = dailyDateSchema.safeParse(dateStr).success
-		? timezone
-			? new Date(`${dateStr}T00:00:00Z`)
-			: new Date(`${dateStr}T00:00:00`)
-		: new Date(dateStr);
+	const isPlainDate = dailyDateSchema.safeParse(dateStr).success;
+	const date = isPlainDate
+		? new Date(`${dateStr}T00:00:00Z`) // Always use UTC for plain dates
+		: new Date(dateStr);

This ensures consistent behavior regardless of the timezone parameter and avoids the type safety issues.


I’d like to inspect the actual implementation and accompanying tests to verify the type-safety and timezone logic. Let’s pull the relevant sections:


🏁 Script executed:

#!/bin/bash
# Show implementation around formatDateCompact
echo "=== src/data-loader.ts (lines 550–600) ==="
sed -n '550,600p' src/data-loader.ts

# Show the new test cases (lines 1440–1490)
echo
echo "=== src/data-loader.ts (lines 1440–1490) ==="
sed -n '1440,1490p' src/data-loader.ts

Length of output: 4662


Approve formatDateCompact logic; address trailing whitespace

The updated implementation correctly distinguishes plain “YYYY-MM-DD” strings from full timestamps and respects the timezone parameter without unsafe type operations on safeParse. Tests confirm the intended behavior across UTC, local dates, and cross-day timezones.

• Remove any trailing spaces in the new test cases (e.g. the Asia/Tokyo line) to satisfy ESLint.
• Verify that all remaining ESLint type-safety complaints are resolved (there are no real unsafe operations on safeParse).

With those minor cleanups, this change is ready to merge.

🧰 Tools
🪛 ESLint

[error] 572-572: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 572-572: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 572-572: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 572-572: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 573-573: Unexpected nullable string value in conditional. Please handle the nullish/empty cases explicitly.

(ts/strict-boolean-expressions)

🤖 Prompt for AI Agents
In src/data-loader.ts around lines 570 to 576, remove any trailing whitespace
characters from the new test cases, especially the Asia/Tokyo line, to comply
with ESLint rules. Also, review the usage of safeParse to ensure no ESLint
type-safety warnings remain by avoiding unsafe type assertions or operations.
These minor cleanups will finalize the changes for merging.

Comment on lines 1443 to 1448

// Daily date defined as UTC is preserved
expect(formatDateCompact('2024-01-01', 'UTC', 'en-US')).toBe('2024\n01-01');

// Daily date already in local time is preserved instead of being interpreted as UTC
expect(formatDateCompact('2024-01-01', undefined, 'en-US')).toBe('2024\n01-01');
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix trailing spaces and improve test assertions

The tests correctly verify the timezone behavior, but there are formatting issues that need to be addressed:

-			
+			
 			// Daily date defined as UTC is preserved
 			expect(formatDateCompact('2024-01-01', 'UTC', 'en-US')).toBe('2024\n01-01');
-			
+			
 			// Daily date already in local time is preserved instead of being interpreted as UTC
 			expect(formatDateCompact('2024-01-01', undefined, 'en-US')).toBe('2024\n01-01');

The tests validate the core functionality well, covering both UTC and local timezone scenarios.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Daily date defined as UTC is preserved
expect(formatDateCompact('2024-01-01', 'UTC', 'en-US')).toBe('2024\n01-01');
// Daily date already in local time is preserved instead of being interpreted as UTC
expect(formatDateCompact('2024-01-01', undefined, 'en-US')).toBe('2024\n01-01');
// Daily date defined as UTC is preserved
expect(formatDateCompact('2024-01-01', 'UTC', 'en-US')).toBe('2024\n01-01');
// Daily date already in local time is preserved instead of being interpreted as UTC
expect(formatDateCompact('2024-01-01', undefined, 'en-US')).toBe('2024\n01-01');
🧰 Tools
🪛 ESLint

[error] 1443-1443: Trailing spaces not allowed.

(style/no-trailing-spaces)


[error] 1445-1445: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1445-1445: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1445-1445: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 1446-1446: Trailing spaces not allowed.

(style/no-trailing-spaces)


[error] 1448-1448: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1448-1448: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1448-1448: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/data-loader.ts around lines 1443 to 1448, fix the trailing spaces in the
test code to improve formatting and readability. Additionally, enhance the test
assertions by making them more explicit or descriptive if possible, ensuring
they clearly validate the expected behavior for both UTC and local timezone
inputs.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/data-loader.ts (1)

1443-1448: Fix formatting issues in test cases

The test logic correctly validates the timezone behavior, but there are formatting issues that need to be addressed:

Based on past review comments and ESLint errors, please clean up the formatting:

-			
+
 			// Daily date defined as UTC is preserved
 			expect(formatDateCompact('2024-01-01', 'UTC', 'en-US')).toBe('2024\n01-01');
-			
+
 			// Daily date already in local time is preserved instead of being interpreted as UTC
 			expect(formatDateCompact('2024-01-01', undefined, 'en-US')).toBe('2024\n01-01');

The ESLint type safety errors on .toBe() calls appear to be false positives similar to the schema validation issues above.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between beb86f5 and 7f8e930.

📒 Files selected for processing (1)
  • src/data-loader.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{js,jsx,ts,tsx}: Lint code using ESLint MCP server (available via Claude Code tools)
Format code with ESLint (writes changes) using bun run format
No console.log allowed except where explicitly disabled with eslint-disable
Do not use console.log. Use logger.ts instead.

Files:

  • src/data-loader.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Type check with TypeScript using bun typecheck

Files:

  • src/data-loader.ts
**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.ts: File paths always use Node.js path utilities for cross-platform compatibility
Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
Use Result.try() for wrapping operations that may throw (JSON parsing, etc.)
Use Result.isFailure() for checking errors (more readable than !Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators
For async operations: create wrapper function with Result.try() then call it
Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
Always use Result.isFailure() and Result.isSuccess() type guards for better code clarity
Variables: start with lowercase (camelCase) - e.g., usageDataSchema, modelBreakdownSchema
Types: start with uppercase (PascalCase) - e.g., UsageData, ModelBreakdown
Constants: can use UPPER_SNAKE_CASE - e.g., DEFAULT_CLAUDE_CODE_PATH
Only export constants, functions, and types that are actually used by other modules
Internal/private constants that are only used within the same file should NOT be exported
Always check if a constant is used elsewhere before making it export const vs just const
All test files must use current Claude 4 models, not outdated Claude 3 models
Test coverage should include both Sonnet and Opus models for comprehensive validation
Model names in tests must exactly match LiteLLM's pricing database entries
When adding new model tests, verify the model exists in LiteLLM before implementation
Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues
Dynamic imports using await import() should only be used within test blocks to avoid tree-shaking issues
Mock data is created using fs-fixture with createFixture() for Claude data directory simulation
In-source testing pattern: Tests are written...

Files:

  • src/data-loader.ts
🧠 Learnings (4)
📚 Learning: applies to **/*.ts : variables: start with lowercase (camelcase) - e.g., `usagedataschema`, `modelbr...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Variables: start with lowercase (camelCase) - e.g., `usageDataSchema`, `modelBreakdownSchema`

Applied to files:

  • src/data-loader.ts
📚 Learning: applies to **/*.ts : mock data is created using `fs-fixture` with `createfixture()` for claude data ...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Mock data is created using `fs-fixture` with `createFixture()` for Claude data directory simulation

Applied to files:

  • src/data-loader.ts
📚 Learning: when aspulse points out that variables are clearly type-inferred as strings from their construction ...
Learnt from: AsPulse
PR: ryoppippi/ccusage#328
File: src/_live-rendering.ts:133-167
Timestamp: 2025-07-20T23:46:21.740Z
Learning: When AsPulse points out that variables are clearly type-inferred as strings from their construction (using template literals, string methods like .toFixed(), .padStart(), etc.), avoid suggesting unnecessary type assertions that would reduce code readability. ESLint type safety errors about "error type" often indicate type definition issues with imported packages rather than actual type problems in the user's code.

Applied to files:

  • src/data-loader.ts
📚 Learning: applies to **/*.ts : dynamic imports using `await import()` should only be used within test blocks t...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Dynamic imports using `await import()` should only be used within test blocks to avoid tree-shaking issues

Applied to files:

  • src/data-loader.ts
🪛 ESLint
src/data-loader.ts

[error] 572-572: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 572-572: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 572-572: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 572-572: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 1445-1445: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1445-1445: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1445-1445: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 1448-1448: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1448-1448: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1448-1448: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/data-loader.ts (2)

570-577: Approve the off-by-one date formatting fix with ESLint cleanup needed

The implementation correctly addresses the off-by-one error by distinguishing plain "YYYY-MM-DD" strings from full timestamps and handling timezone appropriately. The logic properly ensures dates are interpreted as intended (UTC or local time) rather than defaulting to UTC midnight.

However, ESLint is flagging unsafe operations on the safeParse result. Extract the parse result to a variable to enable TypeScript's control-flow narrowing:

-	const date = dailyDateSchema.safeParse(dateStr).success
-		? timezone != null
-			? new Date(`${dateStr}T00:00:00Z`)
-			: new Date(`${dateStr}T00:00:00`)
-		: new Date(dateStr);
+	const parseResult = dailyDateSchema.safeParse(dateStr);
+	const date = parseResult.success
+		? timezone != null
+			? new Date(`${dateStr}T00:00:00Z`)
+			: new Date(`${dateStr}T00:00:00`)
+		: new Date(dateStr);

1444-1449: Fix trailing spaces in test cases

The tests correctly verify the timezone behavior and address the core issue, but there are formatting problems that need to be addressed:

-			
+			
 			// Daily date defined as UTC is preserved
 			expect(formatDateCompact('2024-01-01', 'UTC', 'en-US')).toBe('2024\n01-01');
-			
+			
 			// Daily date already in local time is preserved instead of being interpreted as UTC
 			expect(formatDateCompact('2024-01-01', undefined, 'en-US')).toBe('2024\n01-01');

The test logic validates the intended behavior well, covering both UTC and local timezone scenarios.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f8e930 and a722a45.

📒 Files selected for processing (1)
  • src/data-loader.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{js,jsx,ts,tsx}: Lint code using ESLint MCP server (available via Claude Code tools)
Format code with ESLint (writes changes) using bun run format
No console.log allowed except where explicitly disabled with eslint-disable
Do not use console.log. Use logger.ts instead.

Files:

  • src/data-loader.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Type check with TypeScript using bun typecheck

Files:

  • src/data-loader.ts
**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.ts: File paths always use Node.js path utilities for cross-platform compatibility
Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
Use Result.try() for wrapping operations that may throw (JSON parsing, etc.)
Use Result.isFailure() for checking errors (more readable than !Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators
For async operations: create wrapper function with Result.try() then call it
Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
Always use Result.isFailure() and Result.isSuccess() type guards for better code clarity
Variables: start with lowercase (camelCase) - e.g., usageDataSchema, modelBreakdownSchema
Types: start with uppercase (PascalCase) - e.g., UsageData, ModelBreakdown
Constants: can use UPPER_SNAKE_CASE - e.g., DEFAULT_CLAUDE_CODE_PATH
Only export constants, functions, and types that are actually used by other modules
Internal/private constants that are only used within the same file should NOT be exported
Always check if a constant is used elsewhere before making it export const vs just const
All test files must use current Claude 4 models, not outdated Claude 3 models
Test coverage should include both Sonnet and Opus models for comprehensive validation
Model names in tests must exactly match LiteLLM's pricing database entries
When adding new model tests, verify the model exists in LiteLLM before implementation
Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues
Dynamic imports using await import() should only be used within test blocks to avoid tree-shaking issues
Mock data is created using fs-fixture with createFixture() for Claude data directory simulation
In-source testing pattern: Tests are written...

Files:

  • src/data-loader.ts
🧠 Learnings (4)
📚 Learning: applies to **/*.ts : variables: start with lowercase (camelcase) - e.g., `usagedataschema`, `modelbr...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Variables: start with lowercase (camelCase) - e.g., `usageDataSchema`, `modelBreakdownSchema`

Applied to files:

  • src/data-loader.ts
📚 Learning: applies to **/*.ts : mock data is created using `fs-fixture` with `createfixture()` for claude data ...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Mock data is created using `fs-fixture` with `createFixture()` for Claude data directory simulation

Applied to files:

  • src/data-loader.ts
📚 Learning: when aspulse points out that variables are clearly type-inferred as strings from their construction ...
Learnt from: AsPulse
PR: ryoppippi/ccusage#328
File: src/_live-rendering.ts:133-167
Timestamp: 2025-07-20T23:46:21.740Z
Learning: When AsPulse points out that variables are clearly type-inferred as strings from their construction (using template literals, string methods like .toFixed(), .padStart(), etc.), avoid suggesting unnecessary type assertions that would reduce code readability. ESLint type safety errors about "error type" often indicate type definition issues with imported packages rather than actual type problems in the user's code.

Applied to files:

  • src/data-loader.ts
📚 Learning: applies to **/*.ts : dynamic imports using `await import()` should only be used within test blocks t...
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Dynamic imports using `await import()` should only be used within test blocks to avoid tree-shaking issues

Applied to files:

  • src/data-loader.ts
🧬 Code Graph Analysis (1)
src/data-loader.ts (1)
src/_types.ts (1)
  • dailyDateSchema (30-32)
🪛 ESLint
src/data-loader.ts

[error] 572-572: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 572-572: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 572-572: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 573-573: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 573-573: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 1446-1446: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1446-1446: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1446-1446: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)


[error] 1449-1449: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1449-1449: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1449-1449: Unsafe member access .toBe on an error typed value.

(ts/no-unsafe-member-access)

Copy link
Owner

@ryoppippi ryoppippi left a comment

Choose a reason for hiding this comment

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

LGTM

@ryoppippi ryoppippi changed the title Fix off-by-one compact date formatting fix: off-by-one compact date formatting Aug 12, 2025
@pkg-pr-new
Copy link

pkg-pr-new bot commented Aug 12, 2025

Open in StackBlitz

npm i https://pkg.pr.new/ryoppippi/ccusage@434

commit: a722a45

@ryoppippi ryoppippi merged commit 6d6a3db into ryoppippi:main Aug 12, 2025
9 checks passed
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.

2 participants