user2363025
user2363025

Reputation: 6515

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

I'm trying to get Monday's date of the current week. This is treated as the first day of the week in my table view. I also need to get Sunday's of the current week. This is treated as the last day of the week in my table view.

Current attempt:

let date = NSDate()
let calendar = NSCalendar.currentCalendar()
calendar.firstWeekday = 1
//attempt to changefirstday

let dateFormatter = NSDateFormatter()
let theDateFormat = NSDateFormatterStyle.ShortStyle
let theTimeFormat = NSDateFormatterStyle.ShortStyle
dateFormatter.dateStyle = theDateFormat
dateFormatter.timeStyle = theTimeFormat

let currentDateComponents = calendar.components([.YearForWeekOfYear, .WeekOfYear ], fromDate: date)
let startOfWeek = calendar.dateFromComponents(currentDateComponents)
print("startOfWeek is \(startOfWeek)")
let stringDate = dateFormatter.stringFromDate(startOfWeek!)
print("string date is \(stringDate)") //This is returning Sunday's date

Upvotes: 27

Views: 36642

Answers (8)

Leo Dabus
Leo Dabus

Reputation: 236538

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"

Upvotes: 39

Roi Zakai
Roi Zakai

Reputation: 302

simple code (remember to take better care of the optionals):

let now = Date()

var calendar = Calendar(identifier: .gregorian)

let timeZone = TimeZone(identifier: "UTC")!

let desiredWeekDay = 2

let weekDay = calendar.component(.weekday, from: now)
var weekDayDate = calendar.date(bySetting: .weekday, value: desiredWeekDay, of: now)!

/// Swift will give back the closest day matching the value above so we need to manipulate it to be always included at cuurent week.
if weekDayDate > now, weekDay > desiredWeekDay {
    weekDayDate = weekDayDate - 7*24*60*60
}

print(weekDayDate)

Upvotes: -1

Sandeep
Sandeep

Reputation: 21154

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
      }
    }
  }
}

Upvotes: 97

Rashesh Bosamiya
Rashesh Bosamiya

Reputation: 629

Swift 4 Solution

I have figured out according to my requirement, where I have find out dates for following.

1. Today

2. Tomorrow 

3. This Week 

4. This Weekend 

5. Next Week 

6. Next Weekend

So, I have created Date Extension to get Dates of Current Week and Next Week.

CODE

extension Date {

    func getWeekDates() -> (thisWeek:[Date],nextWeek:[Date]) {
        var tuple: (thisWeek:[Date],nextWeek:[Date])
        var arrThisWeek: [Date] = []
        for i in 0..<7 {
            arrThisWeek.append(Calendar.current.date(byAdding: .day, value: i, to: startOfWeek)!)
        }
        var arrNextWeek: [Date] = []
        for i in 1...7 {
            arrNextWeek.append(Calendar.current.date(byAdding: .day, value: i, to: arrThisWeek.last!)!)
        }
        tuple = (thisWeek: arrThisWeek,nextWeek: arrNextWeek)
        return tuple
    }

    var tomorrow: Date {
        return Calendar.current.date(byAdding: .day, value: 1, to: noon)!
    }
    var noon: Date {
        return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
    }

    var startOfWeek: Date {
        let gregorian = Calendar(identifier: .gregorian)
        let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
        return gregorian.date(byAdding: .day, value: 1, to: sunday!)!
    }

    func toDate(format: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = format
        return formatter.string(from: self)
    }
}

USAGE:

let arrWeekDates = Date().getWeekDates() // Get dates of Current and Next week.
let dateFormat = "MMM dd" // Date format
let thisMon = arrWeekDates.thisWeek.first!.toDate(format: dateFormat)
let thisSat = arrWeekDates.thisWeek[arrWeekDates.thisWeek.count - 2].toDate(format: dateFormat)
let thisSun = arrWeekDates.thisWeek[arrWeekDates.thisWeek.count - 1].toDate(format: dateFormat)

let nextMon = arrWeekDates.nextWeek.first!.toDate(format: dateFormat)
let nextSat = arrWeekDates.nextWeek[arrWeekDates.nextWeek.count - 2].toDate(format: dateFormat)
let nextSun = arrWeekDates.nextWeek[arrWeekDates.nextWeek.count - 1].toDate(format: dateFormat)

print("Today: \(Date().toDate(format: dateFormat))") // Sep 26
print("Tomorrow: \(Date().tomorrow.toDate(format: dateFormat))") // Sep 27
print("This Week: \(thisMon) - \(thisSun)") // Sep 24 - Sep 30
print("This Weekend: \(thisSat) - \(thisSun)") // Sep 29 - Sep 30
print("Next Week: \(nextMon) - \(nextSun)") // Oct 01 - Oct 07
print("Next Weekend: \(nextSat) - \(nextSun)") // Oct 06 - Oct 07

You can modify Extension according to your need.

Thanks!

Upvotes: 2

John
John

Reputation: 1467

Here's a simplified version of Sandeep's answer.

Usage:

Date().next(.monday)
Date().next(.monday, considerToday: true)
Date().next(.monday, direction: .backward)

Extension:

public func next(_ weekday: Weekday,
                 direction: Calendar.SearchDirection = .forward,
                 considerToday: Bool = false) -> Date
{
    let calendar = Calendar(identifier: .gregorian)
    let components = DateComponents(weekday: weekday.rawValue)

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

    return calendar.nextDate(after: self,
                             matching: components,
                             matchingPolicy: .nextTime,
                             direction: direction)!
}

public enum Weekday: Int {
    case sunday = 1, monday, tuesday, wednesday, thursday, friday, saturday
}

Upvotes: 23

Neorcisa
Neorcisa

Reputation: 124

Here is the extension I created, first it finds sunday and then it adds one day

extension Date {  
    var startOfWeek: Date? {
        let gregorian = Calendar(identifier: .gregorian)
        guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
        return gregorian.date(byAdding: .day, value: 1, to: sunday)
    }
}

Upvotes: 8

Muhammad Umair
Muhammad Umair

Reputation: 1714

Addition to @Saneep answer

If you would like to get exact dateTime as per given/current date (lets say you wanted to convert Monday's dateTime -> 23-05-2016 12:00:00 to 23-05-2016 05:35:17) then try this:

func convertDate(date: NSDate, toGivendate: NSDate) -> NSDate {
    let calendar = NSCalendar.currentCalendar()
    let comp = calendar.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: toGivendate)
    let hour = comp.hour
    let minute = comp.minute
    let second = comp.second

    let dateComp = calendar.components([.Year, .Month, .Day], fromDate: date)
    let year = dateComp.year
    let month = dateComp.month
    let day = dateComp.day

    let components = NSDateComponents()
    components.year = year
    components.month = month
    components.day = day
    components.hour = hour
    components.minute = minute
    components.second = second

    let newConvertedDate = calendar.dateFromComponents(components)

    return newConvertedDate!
}

Upvotes: 1

zuziaka
zuziaka

Reputation: 575

Try to use:

calendar.firstWeekday = 2

Edit

To be more specific: NSCalendar.currentCalendar() returns user calendar. According to docs:

The returned calendar is formed from the settings for the current user’s chosen system locale overlaid with any custom settings the user has specified in System Preferences.

If you want always Monday as first day, I think you should use:

let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
calendar!.firstWeekday = 2

Upvotes: 3

Related Questions