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
17 changes: 17 additions & 0 deletions Benchmarks/Benchmarks/Internationalization/BenchmarkCalendar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,23 @@ func calendarBenchmarks() {
}
}

let testDates = {
let date = Date(timeIntervalSince1970: 0)
var dates = [Date]()
dates.reserveCapacity(10000)
for i in 0...10000 {
dates.append(Date(timeInterval: Double(i * 3600), since: date))
}
return dates
}()

Benchmark("NextDatesMatchingOnHour") { _ in
for d in testDates {
let t = currentCalendar.nextDate(after: d, matching: DateComponents(minute: 0, second: 0), matchingPolicy: .nextTime)
blackHole(t)
}
}

// MARK: - Allocations
let reference = Date(timeIntervalSince1970: 1474666555.0) //2016-09-23T14:35:55-0700

Expand Down
4 changes: 4 additions & 0 deletions Sources/FoundationEssentials/Calendar/Calendar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ public struct Calendar : Hashable, Equatable, Sendable {
// The calendar and timeZone properties do not count as a 'highest unit set', since they are not ordered in time like the others are.
return nil
}

var containsOnlyTimeComponents: Bool {
!self.contains(.era) && !self.contains(.year) && !self.contains(.dayOfYear) && !self.contains(.quarter) && !self.contains(.month) && !self.contains(.day) && !self.contains(.weekday) && !self.contains(.weekdayOrdinal) && !self.contains(.weekOfMonth) && !self.contains(.weekOfYear) && !self.contains(.yearForWeekOfYear) && !self.contains(.isLeapMonth) && !self.contains(.isRepeatedDay)
}
}

/// An enumeration for the various components of a calendar date.
Expand Down
31 changes: 18 additions & 13 deletions Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1991,22 +1991,26 @@ internal final class _CalendarGregorian: _CalendarProtocol, @unchecked Sendable
let dateOffsetInSeconds = localDate.timeIntervalSinceReferenceDate.rounded(.down)
let date = Date(timeIntervalSinceReferenceDate: dateOffsetInSeconds) // Round down the given date to seconds

let useJulianRef = useJulianReference(date)
let totalSeconds = Int(dateOffsetInSeconds)
let secondsInDay = (totalSeconds % 86400 + 86400) % 86400

var timeInDay = dateOffsetInSeconds.remainder(dividingBy: 86400) // this has precision of one second
if (timeInDay < 0) {
timeInDay += 86400
}

let hour = Int(timeInDay / 3600) // zero-based
timeInDay = timeInDay.truncatingRemainder(dividingBy: 3600.0)

let minute = Int(timeInDay / 60)
timeInDay = timeInDay.truncatingRemainder(dividingBy: 60.0)

let second = Int(timeInDay)
let hour = secondsInDay / 3600
let minute = (secondsInDay % 3600) / 60
let second = secondsInDay % 60
let nanosecond = Int((localDate.timeIntervalSinceReferenceDate - dateOffsetInSeconds) * 1_000_000_000)

if components.containsOnlyTimeComponents {
var dcHour: Int?
var dcMinute: Int?
var dcSecond: Int?
var dcNano: Int?
if components.contains(.hour) { dcHour = hour }
if components.contains(.minute) { dcMinute = minute }
if components.contains(.second) { dcSecond = second }
if components.contains(.nanosecond) { dcNano = nanosecond }
return DateComponents(hour: dcHour, minute: dcMinute, second: dcSecond, nanosecond: dcNano)
}

let dayOfYear: Int
let weekday: Int
let weekOfMonth: Int
Expand All @@ -2018,6 +2022,7 @@ internal final class _CalendarGregorian: _CalendarProtocol, @unchecked Sendable
var month: Int
var day: Int
do {
let useJulianRef = useJulianReference(date)
let julianDay = try date.julianDay()
(year, month, day) = Self.yearMonthDayFromJulianDay(julianDay, useJulianRef: useJulianRef)
isLeapYear = gregorianYearIsLeap(year)
Expand Down