Home > Blockchain >  Initialising DateComponents with weekOfMonth returning wrong date
Initialising DateComponents with weekOfMonth returning wrong date

Time:10-01

Here is the code:

let components = DateComponents(year: 2022,month: 9,weekOfMonth: 3)
let date = Calendar.autoupdatingCurrent.date(from: components)!
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none
print(formatter.string(from: date))

Here is output:

September 1, 2022

But the correct answer should be September 11, 2022. Why? Is this a bug or I just missed something?

CodePudding user response:

This method will create a date for start of the given year, month and week in month. This will return nil if the start of the week is not in the month specified:

func startOfWeek(in year: Int, month: Int, week: Int, calendar: Calendar = Calendar(identifier: .iso8601)) -> Date?{
    //You would need to set this calendar according when your week starts.
    //For me it´s monday so I need .iso8601
    //For sunday set it to gregorian
    
    var components = DateComponents(calendar: calendar, year: year, month: month, weekOfMonth: week )
    
    //Get the first day of the month. This will probably never fail but....
    guard let firstOfMonth = components.date else{
        return nil
    }
    
    // Handling edge case first of month is start of week
    // if the first is a monday and we look for the first return it
    let startOfWeek = calendar.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: firstOfMonth).date
    if startOfWeek == firstOfMonth, week == 1{
        return firstOfMonth
    }
    // else just ommit the additional week and move on
    else if startOfWeek == firstOfMonth {
        components = DateComponents(calendar: calendar, year: year, month: month, weekOfMonth: week)
    }
    
    // search for the next date starting at first of month according to components given
    return calendar.nextDate(after: firstOfMonth, matching: components, matchingPolicy: .nextTime)
}

Most of the code should be self explanatory.


Testing:

let dates = [(2022, 1, 2), (2022, 2, 1), (2022, 3, 4), (2022, 8, 1), (2022, 9, 1), (2022, 9, 2), (2022, 9, 3), (2022, 9, 4), (2022, 12, 4) , (2021, 11, 5)]

let formatter = DateFormatter()
formatter.dateFormat = "dd MM yyyy"

dates.forEach { year, month, week in
    let date = startOfWeek(in: year, month: month, week: week, calendar: Calendar(identifier: .gregorian))
    print(date != nil ? formatter.string(from: date!) : "no date found")
}

Output:

02 01 2022
no date found
20 03 2022
no date found
no date found
04 09 2022
11 09 2022 <-- thrid week in september 2022
18 09 2022
18 12 2022
28 11 2021
  • Related