3

Today is Friday 6 March. How to find that 16 Feb is the start day and 22 Feb is the end day of the week before previous week. 16 is for my country Bulgaria in USA will be 15 and 21 I use .currentCalendar()

Bogdan Bogdanov
  • 882
  • 11
  • 36
  • 79

4 Answers4

10

Something like this should work:

let cal = NSCalendar.currentCalendar()

let components = NSDateComponents()
components.weekOfYear -= 1

if let date = cal.dateByAddingComponents(components, toDate: NSDate(), options: NSCalendarOptions(0)) {
    var beginningOfWeek: NSDate?
    var weekDuration = NSTimeInterval()
    if cal.rangeOfUnit(.CalendarUnitWeekOfYear, startDate: &beginningOfWeek, interval: &weekDuration, forDate: date) {
        let endOfWeek = beginningOfWeek?.dateByAddingTimeInterval(weekDuration)
        print(beginningOfWeek) // Optional(2015-02-15 05:00:00 +0000)
        print(endOfWeek) // Optional(2015-02-22 05:00:00 +0000)
    }
}
sbooth
  • 16,646
  • 2
  • 55
  • 81
4

A simpler Swift 2 alternative as an extension, derived from Martin R's answer for getting the end of the month:

extension: NSDate {
    func startOfWeek(weekday: Int?) -> NSDate? {
        guard
            let cal: NSCalendar = NSCalendar.currentCalendar(),
            let comp: NSDateComponents = cal.components([.YearForWeekOfYear, .WeekOfYear], fromDate: self) else { return nil }
        comp.to12pm()
        cal.firstWeekday = weekday ?? 1
        return cal.dateFromComponents(comp)!
    }

    func endOfWeek(weekday: Int) -> NSDate? {
        guard
            let cal: NSCalendar = NSCalendar.currentCalendar(),
            let comp: NSDateComponents = cal.components([.WeekOfYear], fromDate: self) else { return nil }
        comp.weekOfYear = 1
        comp.day -= 1
        comp.to12pm()
        return cal.dateByAddingComponents(comp, toDate: self.startOfWeek(weekday)!, options: [])!
    }
}

Usage:

// Use 2 for Monday, 1 for Sunday:
print(NSDate().startOfWeek(1)!) // "2016-01-24 08:00:00 +0000\n"
print(NSDate().endOfWeek(1)!) // "2016-01-30 08:00:00 +0000\n"

To guard against DST and such, you should also implement an extension to force the time to 12 pm:

internal extension NSDateComponents {
    func to12pm() {
        self.hour = 12
        self.minute = 0
        self.second = 0
    }
}
Community
  • 1
  • 1
brandonscript
  • 68,675
  • 32
  • 163
  • 220
4

If anyone looking for swift 3 answer:

func startOfWeek(weekday: Int?) -> Date {
    var cal = Calendar.current
    var component = cal.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
    component.to12am()
    cal.firstWeekday = weekday ?? 1
    return cal.date(from: component)!
}

func endOfWeek(weekday: Int) -> Date {
    let cal = Calendar.current
    var component = DateComponents()
    component.weekOfYear = 1
    component.day = -1
    component.to12pm()
    return cal.date(byAdding: component, to: startOfWeek(weekday: weekday))!
}

I set to 00:00:00 of that day

 internal extension DateComponents {
    mutating func to12am() {
        self.hour = 0
        self.minute = 0
        self.second = 0
    }

    mutating func to12pm(){
        self.hour = 23
        self.minute = 59
        self.second = 59
    }
}
Rob C
  • 366
  • 3
  • 5
  • Shouldn't the endOfWeek also include `component.second = 86399`? – MarksCode Apr 17 '17 at 01:48
  • Yes. I changed answer to set the time to end of day's 23:59:59 – Rob C Apr 17 '17 at 12:52
  • Awesome! Can you help me figure out the formatting so it's "Apr 16 - Apr 23"? And then so it's not Sun - Sat, but Mon - Sun (I can probably hack that by just adding a "+1" to each, but wondering if there was a better way)? – jammyman34 Apr 17 '17 at 18:58
  • Changed the func to12am's "self.hour" to = 0 + 24 and changed all of the to12pm's settings to 0 in order to get the start/end days to be Mon - Sun as apposed to Sun through Sat. Now I just need to get it formatted to "Apr 17 - Apr 23"? – jammyman34 Apr 17 '17 at 20:15
  • ok, so I added this to the Date extension: func monthDay() -> String? { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "MMM dd" return dateFormatter.string(from: self) } Then changed the usage to: "\(String(describing: (Date().startOfWeek(weekday: 1)).monthDay()!)) - \((Date().endOfWeek(weekday: 1)).monthDay()!)" – jammyman34 Apr 17 '17 at 21:38
  • You also need to explicitly set `minimumDaysInFirstWeek` of the `Calendar` to `7` or you might encounter strange bugs. For example, if the date is `June 8, 2017` and the `firstWeekday` is `2` (Monday), your implementation for `startOfWeek()` will return `May 29, 2017` instead of `June 5, 2017`. – Matthew Quiros Jun 11 '17 at 03:15
0

Swift 4+:

A very straightforward approach using mainly Calendar with the help of DateComponents:

  1. Get Date
  2. Go to previous week
    • Calendar.current.date(byAdding: .weekdayOrdinal, value: -1, to: aDate)
  3. Get the current weekday
    • Calendar.current.component(.weekday, from: bDate)
  4. Compute an offset from this weekday
    • Negative offset to reach the start of week
    • Positive offset to the reach end of the week
  5. Use offset to go to the required date
    • Calendar.current.date(byAdding: .day, value: offset, to: bDate)

Solution:

extension Date {
    var firstWeekdayOfLastWeek: Date? {
        guard let previousWeek = Calendar.current.date(byAdding: .weekdayOrdinal,
                                                       value: -1, to: self)
        else { return nil }
        
        let offsetToFirstWeekday: Int = {
            let currentWeekday = Calendar.current.component(.weekday, from: previousWeek)
            guard currentWeekday > 0 else { return 0 }
            return 1 - currentWeekday
        }()
        
        let result = Calendar.current.date(byAdding: .day,
                                           value: offsetToFirstWeekday,
                                           to: previousWeek)
        return result
    }
    
    var lastWeekdayOfLastWeek: Date? {
        guard let previousWeek = Calendar.current.date(byAdding: .weekdayOrdinal,
                                                       value: -1, to: self)
        else { return nil }
        
        let offsetToLastWeekday: Int = {
            let currentWeekday = Calendar.current.component(.weekday, from: previousWeek)
            return 7 - currentWeekday
        }()
        
        let result = Calendar.current.date(byAdding: .day,
                                           value: offsetToLastWeekday,
                                           to: previousWeek)
        return result
    }
}

Usage Example:

let formatter = DateFormatter()
formatter.dateStyle = .long

let date = Date()

//My current system date (as of time of writing)
print(formatter.string(from: currentDate))                  //December 11, 2020

//Results (my system has Sunday as the start of the week)
print(formatter.string(from: date.firstWeekdayOfLastWeek!)) //November 29, 2020
print(formatter.string(from: date.lastWeekdayOfLastWeek!))  //December 5, 2020

Force unwrap is for example purposes only

staticVoidMan
  • 19,275
  • 6
  • 69
  • 98