Skip to content

Handle 1-day weekend properly #1450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 25, 2025
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
17 changes: 13 additions & 4 deletions Sources/FoundationEssentials/Calendar/Calendar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1153,9 +1153,17 @@ public struct Calendar : Hashable, Equatable, Sendable {
}

let weekendEndComponents = DateComponents(weekday: weekend.end)
// We only care about the end date to get the interval of the weekend, so we don't care if it falls ahead of the passed in date. Always search forward from here, since we just found the *beginning* of the weekend.
guard var end = nextDate(after: start, matching: weekendEndComponents, matchingPolicy: .nextTime, repeatedTimePolicy: .first, direction: .forward) else {
return nil

var end: Date
if weekend.start == weekend.end {
// This locale has a 1-day weekend
end = start
} else {
// We only care about the end date to get the interval of the weekend, so we don't care if it falls ahead of the passed in date. Always search forward from here, since we just found the *beginning* of the weekend.
guard let possibleEnd = nextDate(after: start, matching: weekendEndComponents, matchingPolicy: .nextTime, repeatedTimePolicy: .first, direction: .forward) else {
return nil
}
end = possibleEnd
}

if let ceaseTime = weekend.ceaseTime, ceaseTime > 0 {
Expand Down Expand Up @@ -1667,7 +1675,8 @@ package struct WeekendRange: Equatable, Hashable {
package var ceaseTime: TimeInterval?
package var start: Int
package var end: Int


// start == end means a one-day weekend. There isn't a known use case for 7-day weekend.
package init(onsetTime: TimeInterval? = nil, ceaseTime: TimeInterval? = nil, start: Int, end: Int) {
self.onsetTime = onsetTime
self.ceaseTime = ceaseTime
Expand Down
44 changes: 43 additions & 1 deletion Tests/FoundationInternationalizationTests/CalendarTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,49 @@ private struct CalendarTests {
#expect(weekend != nil)
#expect(weekend == weekendForNilLocale)
}


@Test func weekendRange_1dayWeekend() throws {
var calendar = Calendar(identifier: .gregorian)
calendar.locale = Locale(identifier: "en_IN")
calendar.timeZone = .gmt

do {
// Date(timeIntervalSinceReferenceDate: 0) is a Monday
// India's weekend is on Sunday
let weekend = try #require(calendar.nextWeekend(startingAfter: Date(timeIntervalSinceReferenceDate: 0)))
let expectStart = try Date("2001-01-07T00:00:00Z", strategy: .iso8601)
#expect(weekend.start == expectStart)

let expectEnd = try Date("2001-01-08T00:00:00Z", strategy: .iso8601)
#expect(weekend.end == expectEnd)

let previousWeekend = try #require(calendar.nextWeekend(startingAfter: Date(timeIntervalSinceReferenceDate: 0), direction: .backward))
let expectedPreviousStart = try Date("2000-12-31T00:00:00Z", strategy: .iso8601)
#expect(previousWeekend.start == expectedPreviousStart)

let expectedPreviousEnd = try Date("2001-01-01T00:00:00Z", strategy: .iso8601)
#expect(previousWeekend.end == expectedPreviousEnd)
}

// Starting on a weekend
do {
let sundayMidnight = Date(timeIntervalSinceReferenceDate: -86400)
let weekend = try #require(calendar.nextWeekend(startingAfter: sundayMidnight))
let expectStart = try Date("2001-01-07T00:00:00Z", strategy: .iso8601)
#expect(weekend.start == expectStart)

let expectEnd = try Date("2001-01-08T00:00:00Z", strategy: .iso8601)
#expect(weekend.end == expectEnd)

let previousWeekend = try #require(calendar.nextWeekend(startingAfter: sundayMidnight, direction: .backward))
let expectedPreviousStart = try Date("2000-12-24T00:00:00Z", strategy: .iso8601)
#expect(previousWeekend.start == expectedPreviousStart)

let expectedPreviousEnd = try Date("2000-12-25T00:00:00Z", strategy: .iso8601)
#expect(previousWeekend.end == expectedPreviousEnd)
}
}

@Test func datesAdding_range() {
let startDate = Date(timeIntervalSinceReferenceDate: 689292158.712307) // 2022-11-04 22:02:38 UTC
let endDate = startDate + (86400 * 3) + (3600 * 2) // 3 days + 2 hours later - cross a DST boundary which adds a day with an additional hour in it
Expand Down