Last active
April 6, 2023 13:54
-
-
Save warren-gavin/b40a394a8ef419111a920abd27d8fa81 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension Date: Strideable { | |
func distance(to other: Date) -> DateStride { | |
// ... | |
} | |
func advanced(by n: DateStride) -> Date { | |
switch n { | |
case .year(let value), .month(let value), .day(let value): | |
return Calendar.current.date(byAdding: n.component, value: value, to: self)! | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum DateStride { | |
case year(Int) | |
case month(Int) | |
case day(Int) | |
var component: Calendar.Component { | |
switch self { | |
case .year(_): | |
return .year | |
case .month(_): | |
return .month | |
case .day(_): | |
return .day | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension DateStride: SignedNumber { | |
prefix static func -(x: DateStride) -> DateStride { | |
switch x { | |
case .year(let value): | |
return .year(-value) | |
case .month(let value): | |
return .month(-value) | |
case .day(let value): | |
return .day(-value) | |
} | |
} | |
static func -(lhs: DateStride, rhs: DateStride) -> DateStride { | |
switch (lhs, rhs) { | |
case (.year(let lhsValue), .year(let rhsValue)): | |
return .year(lhsValue - rhsValue) | |
case (.month(let lhsValue), .month(let rhsValue)): | |
return .month(lhsValue - rhsValue) | |
case (.day(let lhsValue), .day(let rhsValue)): | |
return .day(lhsValue - rhsValue) | |
default: | |
fatalError("Operation not permitted") | |
} | |
} | |
} | |
// MARK: - ExpressibleByIntegerLiteral | |
extension DateStride { | |
init(integerLiteral value: Int) { | |
self = .day(value) | |
} | |
} | |
// MARK: - Comparable | |
extension DateStride { | |
static func <(lhs: DateStride, rhs: DateStride) -> Bool { | |
switch (lhs, rhs) { | |
case (.year(let lhsValue), .year(let rhsValue)): | |
return lhsValue < rhsValue | |
case (.month(let lhsValue), .month(let rhsValue)): | |
return lhsValue < rhsValue | |
case (.day(let lhsValue), .day(let rhsValue)): | |
return lhsValue < rhsValue | |
default: | |
return false | |
} | |
} | |
static func ==(lhs: DateStride, rhs: DateStride) -> Bool { | |
switch (lhs, rhs) { | |
case (.year(let lhsValue), .year(let rhsValue)): | |
return lhsValue == rhsValue | |
case (.month(let lhsValue), .month(let rhsValue)): | |
return lhsValue == rhsValue | |
case (.day(let lhsValue), .day(let rhsValue)): | |
return lhsValue == rhsValue | |
default: | |
return false | |
} | |
} | |
static func <=(lhs: DateStride, rhs: DateStride) -> Bool { | |
return lhs < rhs || lhs == rhs | |
} | |
static func >=(lhs: DateStride, rhs: DateStride) -> Bool { | |
return !(lhs < rhs) | |
} | |
static func >(lhs: DateStride, rhs: DateStride) -> Bool { | |
return !(lhs <= rhs) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
// MARK: - Offsets and distances | |
extension Date { | |
func distance(between otherDate: Date, unit: Calendar.Component) -> Int { | |
let reducedSelf = reduced(to: unit) | |
let reducedOther = otherDate.reduced(to: unit) | |
return Calendar.current.dateComponents([unit], from: reducedSelf, to: reducedOther).value(for: unit) ?? 0 | |
} | |
func advanced(by value: Int, unit: Calendar.Component) -> Date { | |
return (value == 0 ? self : Calendar.current.date(byAdding: unit, value: value, to: self)!).reduced(to: unit) | |
} | |
private func reduced(to component: Calendar.Component) -> Date { | |
let components: [Calendar.Component] = [.era, .year, .month, .day, .hour, .minute, .second] | |
guard let index = components.index(of: component) else { | |
return self | |
} | |
let componentSet = Set(components[0 ... index]) | |
let calendar = Calendar.current | |
return calendar.date(from: calendar.dateComponents(componentSet, from: self)) ?? self | |
} | |
} | |
// MARK: - Strideable | |
extension Date: Strideable { | |
public func distance(to other: Date) -> DateStride { | |
print("Diat") | |
let selfComponents = Calendar.current.dateComponents([.year, .month, .day], from: self) | |
let otherComponents = Calendar.current.dateComponents([.year, .month, .day], from: other) | |
if selfComponents.year != otherComponents.year { | |
return .year(distance(between: other, unit: .year)) | |
} | |
if selfComponents.month != otherComponents.month { | |
return .month(distance(between: other, unit: .month)) | |
} | |
return .day(distance(between: other, unit: .day)) | |
} | |
public func advanced(by n: DateStride) -> Date { | |
switch n { | |
case .year(let value), .month(let value), .day(let value): | |
return Calendar.current.date(byAdding: n.component, value: value, to: self)! | |
} | |
} | |
} | |
public enum DateStride { | |
case year(Int) | |
case month(Int) | |
case day(Int) | |
var component: Calendar.Component { | |
switch self { | |
case .year(_): return .year | |
case .month(_): return .month | |
case .day(_): return .day | |
} | |
} | |
} | |
extension DateStride: SignedNumber { | |
prefix public static func -(x: DateStride) -> DateStride { | |
switch x { | |
case .year(let value): return .year(-value) | |
case .month(let value): return .month(-value) | |
case .day(let value): return .day(-value) | |
} | |
} | |
public static func -(lhs: DateStride, rhs: DateStride) -> DateStride { | |
switch (lhs, rhs) { | |
case (.year(let lhsValue), .year(let rhsValue)): | |
return .year(lhsValue - rhsValue) | |
case (.month(let lhsValue), .month(let rhsValue)): | |
return .month(lhsValue - rhsValue) | |
case (.day(let lhsValue), .day(let rhsValue)): | |
return .day(lhsValue - rhsValue) | |
default: | |
fatalError("Operation not permitted") | |
} | |
} | |
} | |
// MARK: - ExpressibleByIntegerLiteral | |
public extension DateStride { | |
public init(integerLiteral value: Int) { | |
self = .day(value) | |
} | |
} | |
// MARK: - Comparable | |
public extension DateStride { | |
public static func <(lhs: DateStride, rhs: DateStride) -> Bool { | |
switch (lhs, rhs) { | |
case (.year(let lhsValue), .year(let rhsValue)): | |
return lhsValue < rhsValue | |
case (.month(let lhsValue), .month(let rhsValue)): | |
return lhsValue < rhsValue | |
case (.day(let lhsValue), .day(let rhsValue)): | |
return lhsValue < rhsValue | |
default: | |
return false | |
} | |
} | |
public static func ==(lhs: DateStride, rhs: DateStride) -> Bool { | |
switch (lhs, rhs) { | |
case (.year(let lhsValue), .year(let rhsValue)): | |
return lhsValue == rhsValue | |
case (.month(let lhsValue), .month(let rhsValue)): | |
return lhsValue == rhsValue | |
case (.day(let lhsValue), .day(let rhsValue)): | |
return lhsValue == rhsValue | |
default: | |
return false | |
} | |
} | |
public static func <=(lhs: DateStride, rhs: DateStride) -> Bool { | |
return lhs < rhs || lhs == rhs | |
} | |
public static func >=(lhs: DateStride, rhs: DateStride) -> Bool { | |
return !(lhs < rhs) | |
} | |
public static func >(lhs: DateStride, rhs: DateStride) -> Bool { | |
return !(lhs <= rhs) | |
} | |
} | |
stride(from: Date(), to: Date().addingTimeInterval(100_000_000), by: .month(3)).forEach { print($0) } |
It's SignedNumeric
. SignedNumber
is what it was called back in Swift 3. This gist is from a very old blog post where I discussed making date strideable.
The blog post is obsolete now because Date was made Strideable
in Foundation in iOS 13.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What is SignedNumber?