Skip to content

Commit e5cdcd3

Browse files
author
ComputelessComputer
committed
Use live calendars for day selection
Switch selected-day and day-range logic to Calendar.autoupdatingCurrent so timezone changes update the active day without a restart. Add a rollover test for moving from Los Angeles to Seoul.
1 parent d73ae5a commit e5cdcd3

4 files changed

Lines changed: 37 additions & 20 deletions

File tree

Sources/OpenbirdApp/AppModel.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ final class AppModel: ObservableObject {
374374
}
375375

376376
var googleDocsCaptureHint: GoogleDocsCaptureHint? {
377-
guard Calendar.current.isDateInToday(selectedDay) else {
377+
guard Calendar.autoupdatingCurrent.isDateInToday(selectedDay) else {
378378
return nil
379379
}
380380

@@ -424,7 +424,7 @@ final class AppModel: ObservableObject {
424424
from selectedDay: Date,
425425
previousCurrentDay: Date,
426426
now: Date,
427-
calendar: Calendar = .current
427+
calendar: Calendar = .autoupdatingCurrent
428428
) -> Date? {
429429
let currentDay = calendar.startOfDay(for: now)
430430
guard calendar.isDate(currentDay, inSameDayAs: previousCurrentDay) == false else {
@@ -535,7 +535,7 @@ final class AppModel: ObservableObject {
535535
availableProviderModels = []
536536
}
537537

538-
let dayRange = Calendar.current.dayRange(for: requestedDay)
538+
let dayRange = Calendar.autoupdatingCurrent.dayRange(for: requestedDay)
539539
let day = OpenbirdDateFormatting.dayString(for: requestedDay)
540540
dayLoadStatus = Self.makeDayLoadStatus(
541541
step: 2,
@@ -1300,7 +1300,7 @@ final class AppModel: ObservableObject {
13001300
func sendChat() {
13011301
let question = chatInput.trimmingCharacters(in: .whitespacesAndNewlines)
13021302
guard let thread = chatThread, question.isEmpty == false, isSendingChat == false else { return }
1303-
let requestedDayRange = Calendar.current.dayRange(for: selectedDay)
1303+
let requestedDayRange = Calendar.autoupdatingCurrent.dayRange(for: selectedDay)
13041304

13051305
let userMessage = ChatMessage(threadID: thread.id, role: .user, content: question)
13061306
let assistantPlaceholder = ChatMessage(threadID: thread.id, role: .assistant, content: "")
@@ -1348,7 +1348,7 @@ final class AppModel: ObservableObject {
13481348

13491349
func selectDay(_ day: Date) {
13501350
let normalizedDay = Self.startOfDay(for: day)
1351-
guard Calendar.current.isDate(selectedDay, inSameDayAs: normalizedDay) == false else {
1351+
guard Calendar.autoupdatingCurrent.isDate(selectedDay, inSameDayAs: normalizedDay) == false else {
13521352
return
13531353
}
13541354

@@ -1360,7 +1360,7 @@ final class AppModel: ObservableObject {
13601360
}
13611361

13621362
func handleCurrentDayChangeIfNeeded(now: Date = Date()) {
1363-
let calendar = Calendar.current
1363+
let calendar = Calendar.autoupdatingCurrent
13641364
let currentDay = Self.startOfDay(for: now, calendar: calendar)
13651365
guard calendar.isDate(currentDay, inSameDayAs: currentDayAnchor) == false else {
13661366
return
@@ -1395,7 +1395,7 @@ final class AppModel: ObservableObject {
13951395
return
13961396
}
13971397

1398-
guard Calendar.current.isDate(selectedDay, inSameDayAs: now) else {
1398+
guard Calendar.autoupdatingCurrent.isDate(selectedDay, inSameDayAs: now) else {
13991399
cancelAutomaticJournalGeneration()
14001400
return
14011401
}
@@ -1407,7 +1407,7 @@ final class AppModel: ObservableObject {
14071407

14081408
let requestedDay = selectedDay
14091409
let requestedDayString = OpenbirdDateFormatting.dayString(for: requestedDay)
1410-
let dayRange = Calendar.current.dayRange(for: requestedDay)
1410+
let dayRange = Calendar.autoupdatingCurrent.dayRange(for: requestedDay)
14111411
lastAutomaticSelectedDayRefreshAt = now
14121412

14131413
do {
@@ -1435,7 +1435,7 @@ final class AppModel: ObservableObject {
14351435
guard isShuttingDown == false else {
14361436
return
14371437
}
1438-
guard Calendar.current.isDate(selectedDay, inSameDayAs: Date()) else {
1438+
guard Calendar.autoupdatingCurrent.isDate(selectedDay, inSameDayAs: Date()) else {
14391439
cancelAutomaticJournalGeneration()
14401440
return
14411441
}
@@ -1472,8 +1472,8 @@ final class AppModel: ObservableObject {
14721472
guard isShuttingDown == false else {
14731473
return
14741474
}
1475-
guard Calendar.current.isDate(selectedDay, inSameDayAs: requestedDay),
1476-
Calendar.current.isDate(requestedDay, inSameDayAs: Date()),
1475+
guard Calendar.autoupdatingCurrent.isDate(selectedDay, inSameDayAs: requestedDay),
1476+
Calendar.autoupdatingCurrent.isDate(requestedDay, inSameDayAs: Date()),
14771477
let journal = todayJournal
14781478
else {
14791479
return
@@ -1496,7 +1496,7 @@ final class AppModel: ObservableObject {
14961496
pendingAssistantReply = nil
14971497
}
14981498

1499-
nonisolated private static func startOfDay(for date: Date, calendar: Calendar = .current) -> Date {
1499+
nonisolated private static func startOfDay(for date: Date, calendar: Calendar = .autoupdatingCurrent) -> Date {
15001500
calendar.startOfDay(for: date)
15011501
}
15021502

Sources/OpenbirdApp/TodayView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,18 +361,18 @@ struct TodayView: View {
361361
}
362362

363363
private var selectedDayTitle: String {
364-
let day = Calendar.current.component(.day, from: model.selectedDay)
364+
let day = Calendar.autoupdatingCurrent.component(.day, from: model.selectedDay)
365365
let month = Self.selectedDayMonthString(for: model.selectedDay)
366366
let year = Self.selectedDayYearString(for: model.selectedDay)
367367
return "\(month) \(day)\(ordinalSuffix(for: day)), \(year)"
368368
}
369369

370370
private var isShowingToday: Bool {
371-
Calendar.current.isDate(model.selectedDay, inSameDayAs: Date())
371+
Calendar.autoupdatingCurrent.isDate(model.selectedDay, inSameDayAs: Date())
372372
}
373373

374374
private func stepSelectedDay(by offset: Int) {
375-
let calendar = Calendar.current
375+
let calendar = Calendar.autoupdatingCurrent
376376
let currentDay = calendar.startOfDay(for: model.selectedDay)
377377
let today = calendar.startOfDay(for: Date())
378378

Sources/OpenbirdKit/Storage/OpenbirdStore.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public actor OpenbirdStore {
134134

135135
private func rebuildPreparedActivityEvents(for day: String, date: Date) async throws -> [GroupedActivityEvent] {
136136
let rawEvents = try database.loadActivityEvents(
137-
in: Calendar.current.dayRange(for: date),
137+
in: Calendar.autoupdatingCurrent.dayRange(for: date),
138138
includeExcluded: true
139139
)
140140
let groupedEvents = await Task.detached(priority: .utility) {
@@ -231,7 +231,7 @@ public actor OpenbirdStore {
231231
return []
232232
}
233233

234-
let calendar = Calendar.current
234+
let calendar = Calendar.autoupdatingCurrent
235235
let endOfWindow = calendar.startOfDay(for: date)
236236
guard let startOfWindow = calendar.date(byAdding: .day, value: -(dayCount - 1), to: endOfWindow) else {
237237
return [OpenbirdDateFormatting.dayString(for: endOfWindow)]
@@ -242,7 +242,7 @@ public actor OpenbirdStore {
242242

243243
private func dayStrings(in range: ClosedRange<Date>) -> Set<String> {
244244
var dayStrings: Set<String> = []
245-
let calendar = Calendar.current
245+
let calendar = Calendar.autoupdatingCurrent
246246
var current = calendar.startOfDay(for: range.lowerBound)
247247
let last = calendar.startOfDay(for: range.upperBound)
248248

Tests/OpenbirdAppTests/AppModelDayRolloverTests.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@ struct AppModelDayRolloverTests {
5252
#expect(advancedDay == nil)
5353
}
5454

55+
@Test func advancesSelectedDayWhenTimezoneShiftMovesTodayForward() {
56+
let losAngeles = makeCalendar(timeZoneID: "America/Los_Angeles")
57+
let seoul = makeCalendar(timeZoneID: "Asia/Seoul")
58+
let previousCurrentDay = makeDate(year: 2026, month: 4, day: 4, hour: 0, minute: 0, calendar: losAngeles)
59+
let selectedDay = previousCurrentDay
60+
let now = makeDate(year: 2026, month: 4, day: 5, hour: 9, minute: 0, calendar: seoul)
61+
62+
let advancedDay = AppModel.autoAdvancedSelectedDay(
63+
from: selectedDay,
64+
previousCurrentDay: previousCurrentDay,
65+
now: now,
66+
calendar: seoul
67+
)
68+
69+
#expect(advancedDay == makeDate(year: 2026, month: 4, day: 5, hour: 0, minute: 0, calendar: seoul))
70+
}
71+
5572
@Test func returnsOnlyActivityEventsMissingFromJournalCoverage() {
5673
let calendar = makeCalendar()
5774
let start = makeDate(year: 2026, month: 3, day: 31, hour: 9, minute: 0, calendar: calendar)
@@ -162,9 +179,9 @@ struct AppModelDayRolloverTests {
162179
#expect(uncompiled.isEmpty)
163180
}
164181

165-
private func makeCalendar() -> Calendar {
182+
private func makeCalendar(timeZoneID: String = "UTC") -> Calendar {
166183
var calendar = Calendar(identifier: .gregorian)
167-
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
184+
calendar.timeZone = TimeZone(identifier: timeZoneID) ?? TimeZone(secondsFromGMT: 0)!
168185
return calendar
169186
}
170187

0 commit comments

Comments
 (0)