0

I implemented a function to get the start date of the week as the following.

import UIKit

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = dateFormatter.date(from: "2017-01-07")
let calender = NSCalendar(identifier:NSCalendar.Identifier.gregorian)!
let flags :NSCalendar.Unit = [NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.weekOfYear, NSCalendar.Unit.weekday]

var components = calender.components(flags, from: date!)
components.weekday = 1 // Sunday
components.hour = 0
components.minute = 0
components.second = 0
print(calender.date(from: components)!)

When the date is '2017-05-07', the start date of the week is right.

But when the date is '2017-01-07', the result is '2017-12-30'.

Who know the reason and how to fix.

Thanks

Leo
  • 835
  • 1
  • 12
  • 31
  • The first week of 2017 starts on a Monday. So the previous Sunday is in 2016. Or, the first Sunday in 2017 is `2017-01-08`. – Duncan C May 11 '17 at 15:16
  • @DuncanC: The first Sunday in 2017 was 2017-01-01 – Martin R May 11 '17 at 15:21
  • You have to be very careful about when the week starts. It is Locale dependent. In America we generally say the first day of the week is Sunday, while in other locales the first day of the week is considered to be Monday. This caused an issue once when our application was used by a customer who had his locale set to Ireland and we didn't do our "calendar math" properly. – Scott Thompson May 11 '17 at 15:23
  • @Leo: The Q&A that you linked to provides good solutions. However, it is a different *question* and the answer does not explain what the problem in this question is. – Martin R May 11 '17 at 15:24
  • @MartinR feel free to reopen it and answer it – Leo Dabus May 11 '17 at 15:25
  • I added another duplicate which *I think* is about the same problem. – Martin R May 11 '17 at 15:26
  • @ScottThompson: Indeed. `Calendar` has a `firstWeekday` property which can be read (or set, if necessary). – Martin R May 11 '17 at 15:28
  • @Martin R , I do not think it is the answer for my question, the answer code is the same with mine: extension NSDate { struct Gregorian { static let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! } var startOfWeek: NSDate { return Gregorian.calendar.dateFromComponents(Gregorian.calendar.components([.YearForWeekOfYear, .WeekOfYear ], fromDate: self))! } }. – Leo May 11 '17 at 15:51
  • BTW my code works for any date. It doesn't matter if it is the start of the year – Leo Dabus May 11 '17 at 16:09
  • Looks like you are mixing Swift 2 and 3 classes. You should use Calendar instead of NSCalendar – Leo Dabus May 11 '17 at 16:12
  • to get the components in Swift 3 you need to use dateComponents method on Calendar instead of components in NSCalendar – Leo Dabus May 11 '17 at 16:14
  • @Leo: I am fairly sure that both Leo's and my answer works correctly for your date. In my test it prints `2016-12-31 23:00:00 +0000` (UTC), which is `2017-01-01` in my time zone, and that is the Sunday preceding `2017-01-07`. – Martin R May 11 '17 at 16:16
  • @LeoDabus,@Martin R, Thanks very much, you are right, and LeoDabus's explanation is perfect, It is caused by I used Date and NSCalendar. I should use Calendar when Date, and using NSCalendar when NSDate. Very awkward!!! – Leo May 11 '17 at 16:32
  • There *is* another quirk here... the original method used by @Leo *should* work (even with the Swift 2 / 3 mismatching), yet the (incorrect) result is `2017-12-30`. To explain why: the set of Component Flags used does not include `.weekdayOrdinal` ... according to Apple docs, it is then *undefined* (returns `nil`) and therefore produces the wrong date. Simply adding that to @Leo original code fixes the erroneous date. (of course, the solutions from both @MartinR and @LeoDabus are much better). – DonMag May 11 '17 at 16:36
  • @DonMag your answer is great, “include .weekdayOrdinal ... according to Apple docs,” Can you share me the Doc. – Leo May 11 '17 at 16:55
  • @Leo: Also with `flags = [NSCalendar.Unit.yearForWeekOfYear, NSCalendar.Unit.weekOfYear]` your code produces the correct result. So the problem is *not* the Date/NSDate or Calendar/NSCalendar or Swift 2/3 mix, but the wrong set of calendar components (as I tried to explain in http://stackoverflow.com/a/33243719/1187415) – Martin R May 11 '17 at 16:58
  • @Leo - reading through this document is what clued me in to the missing `.weekdayOrdinal` - https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/DatesAndTimes/Articles/dtCalendars.html#//apple_ref/doc/uid/TP40003470 – DonMag May 11 '17 at 17:33
  • @DonMag If you would like to change the firstWeekday you should set it at your calendar object `var calendar = Calendar(identifier: .gregorian) calendar.firstWeekday = 2 let monday = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date))! // "Jan 2, 2017, 12:00 AM"` – Leo Dabus May 11 '17 at 17:36

0 Answers0