Search code examples
cocoacalendarfoundation

Using Apple Foundation Calendar properly


I'm a little bit confused about the correct usage of Calendar of Apple Foundations framework.

let calendar = Calendar(identifier: .iso8601)
let dayComponent = DateComponents(year: 2019, weekday: 1, weekOfYear: 6)
let date = calendar.date(from: dayComponent)

I need to get the first day of a given week of year. When using the code above the following dates are given depending on weekday:

//weekday:
//0 -> 08 FEB
//1 -> 09 FEB
//2 -> 03 FEB
//3 -> 04 FEB
//4 -> 05 FEB

Why does weekday starts with 0 at the current week (6) while switching to week 5 when increased?

Thanks for any help.


Solution

  • A couple of observations:

    1. When iterate through weekdays, you want to go from 1 through 7, because, “Day, week, weekday, month, and year numbers are generally 1-based...” Date and Time Programming Guide: Date Components and Calendar Units. You can use range(of:in:for:), maximumRange(of:), etc., to find the range of possible values.

    2. The weekday values from 1 through 7 do not mean “first day of the week”, “second day of the week”, etc. They refer to specific days of the week, e.g. for .iso8601, “Sun” is 1, “Mon” is 2, etc.

    3. Make sure when you use weekOfYear, you use yearForWeekOfYear:

      let calendar = Calendar(identifier: .iso8601)
      let firstOfWeek = DateComponents(calendar: calendar, weekOfYear: 6, yearForWeekOfYear: 2019).date!
      
    4. Your code is iterating through the weekdays. Consider this code that enumerates all of the days of the week for the sixth week of 2019 (i.e. the week starting Monday, February 4th and ending Sunday, February 10th):

      let weekdays = calendar.range(of: .weekday, in: .weekOfYear, for: firstOfWeek)!
      let daysOfTheWeek = Dictionary(uniqueKeysWithValues: zip(weekdays, calendar.weekdaySymbols))
      
      for weekday in weekdays {
          let date = DateComponents(calendar: calendar, weekday: weekday, weekOfYear: 6, yearForWeekOfYear: 2019).date!
          print("The", daysOfTheWeek[weekday]!, "in the 6th week of 2019 is", formatter.string(from: date))
      }
      

      That results in:

      The Sun in the 6th week of 2019 is Sunday, February 10, 2019
      The Mon in the 6th week of 2019 is Monday, February 4, 2019
      The Tue in the 6th week of 2019 is Tuesday, February 5, 2019
      The Wed in the 6th week of 2019 is Wednesday, February 6, 2019
      The Thu in the 6th week of 2019 is Thursday, February 7, 2019
      The Fri in the 6th week of 2019 is Friday, February 8, 2019
      The Sat in the 6th week of 2019 is Saturday, February 9, 2019

      This is effectively what your code does and why you’re not seeing what you expected.

    5. If you want iterate through the seven days of the week in order, just get the start of the week and then offset it from there:

      let calendar = Calendar(identifier: .iso8601)
      let startOfWeek = DateComponents(calendar: calendar, weekOfYear: 6, yearForWeekOfYear: 2019).date!
      
      for offset in 0 ..< 7 {
          let date = calendar.date(byAdding: .day, value: offset, to: startOfWeek)!
          print(offset, "->", formatter.string(from: date))
      }
      

      That results in:

      0 -> Monday, February 4, 2019
      1 -> Tuesday, February 5, 2019
      2 -> Wednesday, February 6, 2019
      3 -> Thursday, February 7, 2019
      4 -> Friday, February 8, 2019
      5 -> Saturday, February 9, 2019
      6 -> Sunday, February 10, 2019

    6. You asked:

      I need to get the first day of a given week of year.

      Probably needless to say at this point, but just omit the weekday:

      let startOfWeek = DateComponents(calendar: calendar, weekOfYear: 6, yearForWeekOfYear: 2019).date!
      

    Also see Date and Time Programming Guide: Week-Based Calendars.