Klas Zetterlund Klas Zetterlund - 1 year ago 66
Swift Question

Get number of weeks for a given ISO 8601 calendar year

I need to make a function for calculating the number of weeks of a given year in swift, according to the ISO-week-system (ISO8601). I need the number as an Int. First I figured I need to make a NSDate for december 31st and just ask what week that's in, then I realized sometimes december 31st is in week 1 of the following year (for example december 31st 2014)
Anybody know of a way to do this?

I tried

var calendarr = NSCalendar(calendarIdentifier: NSCalendar.Identifier.ISO8601)!
var monday = "01-01-2005"

var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
var datumet = dateFormatter.date(from: monday)
var weekRange = calendarr.range(of: .weekOfYear, in: .year, for: datumet!)
var weeksCount = weekRange.length

print("this many weeks", weeksCount)


But it gives me 53 weeks, no matter what year I put in. As a reference 2004 had 53 weeks and 2005 had 52 weeks

Answer Source

A simple formula to determine the number of weeks in a year of the ISO calendar can be found in Long and Short ISO Calendar Years:

An ISO calendar year y is long (contains 53 weeks) if:
- p(y) modulo 7 = 4
or if:
- p(y–1) modulo 7 = 3
with:
- p(y) = y + floor(y/4) – floor(y/100) + floor(y/400)

Note that the version in Wikipedia: Weeks per year seems to have a small error, "if p(year) = 5" should be "if p(year) = 4".

This is an implementation in Swift:

func weeks(in year: Int) -> Int {
    func p(_ year: Int) -> Int {
        return (year + year/4 - year/100 + year/400) % 7
    }
    return (p(year) == 4 || p(year-1) == 3) ? 53 : 52
}

Test:

print(weeks(in: 2015)) // 53
print(weeks(in: 2016)) // 52

let longYears = (2000...2100).filter({ weeks(in: $0) == 53 })
print(longYears)
// [2004, 2009, 2015, 2020, 2026, 2032, 2037, 2043, 2048, 2054, 2060, 2065, 2071, 2076, 2082, 2088, 2093, 2099]

which coincides with the list given in the Wikipedia article.


Re your edit: Your attempted code is almost correct. As @vadian already said in a comment, it should be

let weekRange = calendarr.range(of: .weekOfYear, in: .yearForWeekOfYear, for: datumet!)

In addition, January 1st may fall into the previous ISO year (e.g. for the year 2016). Choosing February 1st as the date in the given year should solve that problem.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download