The answer from @ipje did the trick for the next 5 minutes but I needed something more flexible and I wanted to get rid of all the magic numbers.
I found a solution thanks to an answer to a similar question
My solution uses the Swift 5.2 and Measurement
to avoid using magic numbers:
extension UnitDuration {
var upperUnit: Calendar.Component? {
if self == .nanoseconds {
return .second
}
if self == .seconds {
return .minute
}
if self == .minutes {
return .hour
}
if self == .hours {
return .day
}
return nil
}
}
extension Date {
func roundDate(to value: Int, in unit: UnitDuration, using rule: FloatingPointRoundingRule, and calendar: Calendar = Calendar.current) -> Date? {
guard unit != .picoseconds && unit != .nanoseconds,
let upperUnit = unit.upperUnit else { return nil }
let value = Double(value)
let unitMeasurement = Measurement(value: value, unit: unit)
let interval = unitMeasurement.converted(to: .seconds).value
let startOfPeriod = calendar.dateInterval(of: upperUnit, for: self)!.start
var seconds = self.timeIntervalSince(startOfPeriod)
seconds = (seconds / interval).rounded(rule) * interval
return startOfPeriod.addingTimeInterval(seconds)
}
func roundDate(toNearest value: Int, in unit: UnitDuration, using calendar: Calendar = Calendar.current) -> Date? {
return roundDate(to: value, in: unit, using: .toNearestOrEven)
}
func roundDate(toNext value: Int, in unit: UnitDuration, using calendar: Calendar = Calendar.current) -> Date? {
return roundDate(to: value, in: unit, using: .up)
}
}
In my playground :
let calendar = Calendar.current
let date = Calendar.current.date(from: DateComponents(timeZone: TimeZone.current, year: 2020, month: 6, day: 12, hour: 00, minute: 24, second: 17, nanosecond: 577881))! // 12 Jun 2020 at 00:24
var roundedDate = date.roundDate(toNext: 5, in: .seconds)!
//"12 Jun 2020 at 00:24"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 24 second: 20 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNearest: 5, in: .seconds)!
// "12 Jun 2020 at 00:24"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 24 second: 15 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNext: 5, in: .minutes)!
// "12 Jun 2020 at 00:25"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 25 second: 0 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNearest: 5, in: .minutes)!
// "12 Jun 2020 at 00:25"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 25 second: 0 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNext: 5, in: .hours)!
// "12 Jun 2020 at 05:00"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 5 minute: 0 second: 0 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNearest: 5, in: .hours)!
// "12 Jun 2020 at 00:00"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 0 second: 0 nanosecond: 0 isLeapMonth: false