How to get Monday's date of the current week in swift

Solution 1:

I wrote Date extensions to get Date for certain weekday and here is how easy it is to use with Swift 5,

Date.today()                                  // Oct 15, 2019 at 9:21 AM
Date.today().next(.monday)                    // Oct 21, 2019 at 9:21 AM
Date.today().next(.sunday)                    //  Oct 20, 2019 at 9:21 AM


Date.today().previous(.sunday)                // Oct 13, 2019 at 9:21 AM
Date.today().previous(.monday)                // Oct 14, 2019 at 9:21 AM

Date.today().previous(.thursday)              // Oct 10, 2019 at 9:21 AM
Date.today().next(.thursday)                  // Oct 17, 2019 at 9:21 AM
Date.today().previous(.thursday,
                      considerToday: true)    // Oct 10, 2019 at 9:21 AM


Date.today().next(.monday)
            .next(.sunday)
            .next(.thursday)                  // Oct 31, 2019 at 9:21 AM

And here is Date extension for that,

extension Date {

  static func today() -> Date {
      return Date()
  }

  func next(_ weekday: Weekday, considerToday: Bool = false) -> Date {
    return get(.next,
               weekday,
               considerToday: considerToday)
  }

  func previous(_ weekday: Weekday, considerToday: Bool = false) -> Date {
    return get(.previous,
               weekday,
               considerToday: considerToday)
  }

  func get(_ direction: SearchDirection,
           _ weekDay: Weekday,
           considerToday consider: Bool = false) -> Date {

    let dayName = weekDay.rawValue

    let weekdaysName = getWeekDaysInEnglish().map { $0.lowercased() }

    assert(weekdaysName.contains(dayName), "weekday symbol should be in form \(weekdaysName)")

    let searchWeekdayIndex = weekdaysName.firstIndex(of: dayName)! + 1

    let calendar = Calendar(identifier: .gregorian)

    if consider && calendar.component(.weekday, from: self) == searchWeekdayIndex {
      return self
    }

    var nextDateComponent = calendar.dateComponents([.hour, .minute, .second], from: self)
    nextDateComponent.weekday = searchWeekdayIndex

    let date = calendar.nextDate(after: self,
                                 matching: nextDateComponent,
                                 matchingPolicy: .nextTime,
                                 direction: direction.calendarSearchDirection)

    return date!
  }

}

// MARK: Helper methods
extension Date {
  func getWeekDaysInEnglish() -> [String] {
    var calendar = Calendar(identifier: .gregorian)
    calendar.locale = Locale(identifier: "en_US_POSIX")
    return calendar.weekdaySymbols
  }

  enum Weekday: String {
    case monday, tuesday, wednesday, thursday, friday, saturday, sunday
  }

  enum SearchDirection {
    case next
    case previous

    var calendarSearchDirection: Calendar.SearchDirection {
      switch self {
      case .next:
        return .forward
      case .previous:
        return .backward
      }
    }
  }
}

Solution 2:

You can use calendar ISO8601 where the first weekday is Monday:

Swift 5.2 or later

extension Calendar {
    static let iso8601 = Calendar(identifier: .iso8601)
    static let iso8601UTC: Calendar = {
        var calendar = Calendar(identifier: .iso8601)
        calendar.timeZone = TimeZone(identifier: "UTC")!
        return calendar
    }()
}

let monday =
    Calendar.iso8601.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: Date()).date!  // "Nov 9, 2020 at 12:00 AM"
print(monday.description(with: .current))   // "Monday, November 9, 2020 at 12:00:00 AM Brasilia Standard Time\n"
let mondayUTC =
    Calendar.iso8601UTC.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: Date()).date!  // "Nov 8, 2020 at 9:00 PM" TimeZone -03:00
print(mondayUTC)   // "2020-11-09 00:00:00 +0000\n"

Implemented as a Date computer property extension:

extension Date {
    var mondayOfTheSameWeek: Date {
        Calendar.iso8601.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: self).date!
    }
    var mondayOfTheSameWeekAtUTC: Date {
        Calendar.iso8601UTC.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: self).date!
    }
}

let mondayOfTheSameWeek = Date().mondayOfTheSameWeek   // "Nov 9, 2020 at 12:00 AM"
print(mondayOfTheSameWeek.description(with: .current)) // "Monday, November 9, 2020 at 12:00:00 AM Brasilia Standard Time\n"
let mondayOfTheSameWeekAtUTC = Date().mondayOfTheSameWeekAtUTC  // "Nov 8, 2020 at 9:00 PM"
print(mondayOfTheSameWeekAtUTC) // "2020-11-09 00:00:00 +0000\n"