2

Is there any convenient way in Swift to say that, for example, 15 months is more than 1 year and 1 week is less than 10 days? I feel like DateComponents represents my needs best, so I need something like:

DateComponents(year: 1) > DateComponents(month: 15) // => false
DateComponents(day: 10) > DateComponents(weekOfMonth: 1) // => true

But currently in swift DateComponents are not comparable (Binary operator '>' cannot be applied to two 'DateComponents' operands), as I understand.

So maybe anyone can help me to find the solution with pure swift, or using some library? Thank you in advance!

Egor Iskrenkov
  • 433
  • 4
  • 15
  • 1
    Seems like an x-y question? What are you _really_ trying to do? – matt Oct 24 '20 at 16:49
  • @JoakimDanielson DateComponents do support month values greater than 12 – Leo Dabus Oct 24 '20 at 16:57
  • @matt it's hard to describe without you using my app tbh :) For example, think of it like one of your subscriptions (Spotify, YouTube, whatever) cycles, they can be monthly, yearly and so on. So I just want to know, which one of your subscriptions has the longest cycle having only Calendar.Component (month/year) and Int (amount of month/year) – Egor Iskrenkov Oct 24 '20 at 17:11
  • 4
    "So I just want to know, which one of your subscriptions has the longest cycle having only Calendar.Component (month/year) and Int (amount of month/year)" It would have been great to ask _that_ question! – matt Oct 24 '20 at 17:30

1 Answers1

4

You can create dates from the DateComponents and compare them. You can make DateComponents conform to Comparable:

extension DateComponents: Comparable {
    public static func < (lhs: DateComponents, rhs: DateComponents) -> Bool {
        let now = Date()
        let calendar = Calendar.current
        return calendar.date(byAdding: lhs, to: now)! < calendar.date(byAdding: rhs, to: now)!
    }
}
    

Then you can do those comparisons:

DateComponents(year: 1) > DateComponents(month: 15) // => false
DateComponents(day: 10) > DateComponents(weekOfMonth: 1) // => true

You might also want to make it Equatable:

extension DateComponents: Equatable {
    public static func == (lhs: DateComponents, rhs: DateComponents) -> Bool {
        let now = Date()
        let calendar = Calendar.current
        return calendar.date(byAdding: lhs, to: now)! == calendar.date(byAdding: rhs, to: now)!
    }
}

Disclaimer: This revised answer is using the current date/time as the reference to ensure meaningful comparison of days and months (give that the number of days per month can change). Questions like “are there more than 30 days in a month” only makes sense if the caller supplies a reference date or we use “now” (which is what I’ve done above).

Note, by using “now” as the reference date, then comparisons like “which is greater, 24 hours or 1 day” will now incorporate daylight savings (e.g., depending upon whether your calendar will “spring forward”, “fall back”, or, the vast majority of the time, not change at all).

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 2
    this is a better approach. I will delete my post since you can't construct a date from just the weekOfMonth – Leo Dabus Oct 24 '20 at 17:15
  • Played with it a bit, can't find any downsides for me, will use this, thank you! – Egor Iskrenkov Oct 24 '20 at 17:22
  • 1970 isn’t specifically helpful here; suggest just using Date’s representation of zero. –  Oct 24 '20 at 17:24
  • @Jessy I think a better option would be using now – Leo Dabus Oct 24 '20 at 17:25
  • 2
    Yeah, but default is doing element wise comparison, where date components of 1 year is not equal to 12 months. – Rob Oct 24 '20 at 17:28
  • Re zero vs now vs reference date, it just depends upon the desired behavior. I wanted a stable comparison of days and months (given that number of days per month can change), but in retrospect, using now is the only meaningful solution. Using either .zero or reference date is too arbitrary re days/month. – Rob Oct 24 '20 at 17:33
  • Also note that date components don't really give enough information for a correct comparison (consider daylight saving time changes). – Sulthan Oct 24 '20 at 17:34