Saturday, 6 October 2018

Get first Monday in Calendar month

I am trapped in a Calendar/TimeZone/DateComponents/Date hell and my mind is spinning.

I am creating a diary app and I want it to work worldwide (with a iso8601 calendar).

I am trying to create a calendar view on iOS similar to the macOS calendar so that my user can navigate their diary entries (notice the 7x6 grid for each month):

/Users/adamwaite/Desktop/Screenshot 2018-10-04 at 08.22.24.png

To get the 1st of the month I can do something like the following:

func firstOfMonth(month: Int, year: Int) -> Date {

  let calendar = Calendar(identifier: .iso8601)

  var firstOfMonthComponents = DateComponents()
  // firstOfMonthComponents.timeZone = TimeZone(identifier: "UTC") // <- fixes daylight savings time
  firstOfMonthComponents.calendar = calendar
  firstOfMonthComponents.year = year
  firstOfMonthComponents.month = month
  firstOfMonthComponents.day = 01

  return firstOfMonthComponents.date!

}

(1...12).forEach {

  print(firstOfMonth(month: $0, year: 2018))

  /*

   Gives:

   2018-01-01 00:00:00 +0000
   2018-02-01 00:00:00 +0000
   2018-03-01 00:00:00 +0000
   2018-03-31 23:00:00 +0000
   2018-04-30 23:00:00 +0000
   2018-05-31 23:00:00 +0000
   2018-06-30 23:00:00 +0000
   2018-07-31 23:00:00 +0000
   2018-08-31 23:00:00 +0000
   2018-09-30 23:00:00 +0000
   2018-11-01 00:00:00 +0000
   2018-12-01 00:00:00 +0000
*/

}

There's an immediate issue here with daylight savings time. That issue can be "fixed" by uncommenting the commented line and forcing the date to be calculated in UTC. I feel as though by forcing it to UTC the dates become invalid when viewing the calendar view in different time zones.

The real question is though: How do I get the first Monday in the week containing the 1st of the month? For example, how do I get Monday 29th February, or Monday 26th April? (see the macOS screenshot). To get the end of the month, do I just add on 42 days from the start? Or is that naive?

Edit

Thanks to the current answers, but we're still stuck.

The following works, until you take daylight savings time into account:

almost



from Get first Monday in Calendar month

No comments:

Post a Comment