Skip to content

Commit 70ef850

Browse files
authored
fix: roll Claude reset dates across the year boundary (#1947)
Roll yearless Claude quota reset dates into the next year when their current-year interpretation is already past, covering both minute-bearing and hour-only formats. Co-authored-by: Yash Raj Pandey <yashpn62@gmail.com>
1 parent 1c80b20 commit 70ef850

3 files changed

Lines changed: 30 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
### Fixed
99
- Amp: open the current Usage page from the menu dashboard action. Thanks @3kh0!
10+
- Claude: keep yearless reset dates in the upcoming year when a quota crosses New Year's Day. Thanks @devYRPauli!
1011
- Settings: keep Language, Default Terminal, and Refresh cadence selectors interactive on macOS 27.
1112
- Usage formatting: show every positive sub-1% value as `<1%` instead of rounding values above 0.5% up to `1%`. Thanks @devYRPauli!
1213
- Codex menu: hide error-only optional Credits and OpenAI web setup diagnostics while keeping them visible in provider Settings.

Sources/CodexBarCore/Providers/Claude/ClaudeStatusProbe.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,13 +628,13 @@ public struct ClaudeStatusProbe: Sendable {
628628
if let date = self.parseDate(raw, formats: Self.resetDateTimeWithMinutes, formatter: formatter) {
629629
var comps = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date)
630630
comps.second = 0
631-
return calendar.date(from: comps)
631+
return self.bumpYearIfNeeded(calendar.date(from: comps), now: now, calendar: calendar)
632632
}
633633
if let date = self.parseDate(raw, formats: Self.resetDateTimeHourOnly, formatter: formatter) {
634634
var comps = calendar.dateComponents([.year, .month, .day, .hour], from: date)
635635
comps.minute = 0
636636
comps.second = 0
637-
return calendar.date(from: comps)
637+
return self.bumpYearIfNeeded(calendar.date(from: comps), now: now, calendar: calendar)
638638
}
639639

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

662+
/// Yearless dates parsed just before New Year's can otherwise land nearly a year in the past.
663+
private static func bumpYearIfNeeded(_ date: Date?, now: Date, calendar: Calendar) -> Date? {
664+
guard let date else { return nil }
665+
if date >= now { return date }
666+
return calendar.date(byAdding: .year, value: 1, to: date)
667+
}
668+
662669
private static let resetTimeWithMinutes = ["h:mma", "h:mm a", "HH:mm", "H:mm"]
663670
private static let resetTimeHourOnly = ["ha", "h a"]
664671

Tests/CodexBarTests/StatusProbeTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,26 @@ struct StatusProbeTests {
659659
#expect(parsed == expected)
660660
}
661661

662+
@Test
663+
func `rolls claude reset date forward across the year boundary`() throws {
664+
var calendar = Calendar(identifier: .gregorian)
665+
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
666+
let now = try #require(calendar.date(from: DateComponents(
667+
year: 2026, month: 12, day: 31, hour: 23, minute: 0, second: 0)))
668+
let cases = [
669+
("Resets Jan 2, 3:15am (UTC)", 15),
670+
("Resets Jan 2, 3am (UTC)", 0),
671+
]
672+
673+
for (text, minute) in cases {
674+
let parsed = ClaudeStatusProbe.parseResetDate(from: text, now: now)
675+
let expected = calendar.date(from: DateComponents(
676+
year: 2027, month: 1, day: 2, hour: 3, minute: minute, second: 0))
677+
#expect(parsed == expected, "Failed to roll forward: \(text)")
678+
#expect(try #require(parsed) > now)
679+
}
680+
}
681+
662682
@Test
663683
func `parses claude reset with dot separated time`() throws {
664684
let now = Date(timeIntervalSince1970: 1_733_690_000)

0 commit comments

Comments
 (0)