Skip to content

Instantly share code, notes, and snippets.

@warren-gavin
Last active April 6, 2023 13:54
Show Gist options
  • Save warren-gavin/b40a394a8ef419111a920abd27d8fa81 to your computer and use it in GitHub Desktop.
Save warren-gavin/b40a394a8ef419111a920abd27d8fa81 to your computer and use it in GitHub Desktop.
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)!
}
}
}
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 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)
}
}
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) }
@TakasurAtWork
Copy link

What is SignedNumber?

@warren-gavin
Copy link
Author

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