Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

### Fixed
- Amp: open the current Usage page from the menu dashboard action. Thanks @3kh0!
- Claude: keep yearless reset dates in the upcoming year when a quota crosses New Year's Day. Thanks @devYRPauli!
- Settings: keep Language, Default Terminal, and Refresh cadence selectors interactive on macOS 27.
- Usage formatting: show every positive sub-1% value as `<1%` instead of rounding values above 0.5% up to `1%`. Thanks @devYRPauli!
- Codex menu: hide error-only optional Credits and OpenAI web setup diagnostics while keeping them visible in provider Settings.
Expand Down
11 changes: 9 additions & 2 deletions Sources/CodexBarCore/Providers/Claude/ClaudeStatusProbe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -628,13 +628,13 @@ public struct ClaudeStatusProbe: Sendable {
if let date = self.parseDate(raw, formats: Self.resetDateTimeWithMinutes, formatter: formatter) {
var comps = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date)
comps.second = 0
return calendar.date(from: comps)
return self.bumpYearIfNeeded(calendar.date(from: comps), now: now, calendar: calendar)
}
if let date = self.parseDate(raw, formats: Self.resetDateTimeHourOnly, formatter: formatter) {
var comps = calendar.dateComponents([.year, .month, .day, .hour], from: date)
comps.minute = 0
comps.second = 0
return calendar.date(from: comps)
return self.bumpYearIfNeeded(calendar.date(from: comps), now: now, calendar: calendar)
}

if let time = self.parseDate(raw, formats: Self.resetTimeWithMinutes, formatter: formatter) {
Expand All @@ -659,6 +659,13 @@ public struct ClaudeStatusProbe: Sendable {
return calendar.date(byAdding: .day, value: 1, to: anchored)
}

/// Yearless dates parsed just before New Year's can otherwise land nearly a year in the past.
private static func bumpYearIfNeeded(_ date: Date?, now: Date, calendar: Calendar) -> Date? {
guard let date else { return nil }
if date >= now { return date }
return calendar.date(byAdding: .year, value: 1, to: date)
Comment on lines +665 to +666

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 Avoid rolling stale reset times a full year

When the status probe crosses a reset boundary, this now treats any parsed date that is even seconds in the past as the same month/day next year. fetch() captures /usage first and only later parses the reset strings after the optional /status probe, so a weekly line like Resets Jul 6, 3:00pm captured just before 3:00 but parsed just after 3:00 becomes July 6 of the following year instead of a stale/expired reset. That leaves menus/countdowns with a reset hundreds of days out; please restrict the year bump to plausible Dec→Jan wraparound cases, such as when the bumped date is within the Claude reset window.

Useful? React with 👍 / 👎.

}

private static let resetTimeWithMinutes = ["h:mma", "h:mm a", "HH:mm", "H:mm"]
private static let resetTimeHourOnly = ["ha", "h a"]

Expand Down
20 changes: 20 additions & 0 deletions Tests/CodexBarTests/StatusProbeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,26 @@ struct StatusProbeTests {
#expect(parsed == expected)
}

@Test
func `rolls claude reset date forward across the year boundary`() throws {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
let now = try #require(calendar.date(from: DateComponents(
year: 2026, month: 12, day: 31, hour: 23, minute: 0, second: 0)))
let cases = [
("Resets Jan 2, 3:15am (UTC)", 15),
("Resets Jan 2, 3am (UTC)", 0),
]

for (text, minute) in cases {
let parsed = ClaudeStatusProbe.parseResetDate(from: text, now: now)
let expected = calendar.date(from: DateComponents(
year: 2027, month: 1, day: 2, hour: 3, minute: minute, second: 0))
#expect(parsed == expected, "Failed to roll forward: \(text)")
#expect(try #require(parsed) > now)
}
}

@Test
func `parses claude reset with dot separated time`() throws {
let now = Date(timeIntervalSince1970: 1_733_690_000)
Expand Down