0

I want to get the first day and the last day of the week. But my results do not match the documentation from apple: https://developer.apple.com/documentation/foundation/nsdatecomponents/1410442-weekday

This is my function:

func startAndEndDateOfWeek(weekOfYearWithYear: (week: Int,year: Int)) -> (start: Date, end: Date) {
    var output = (start: Date.init(), end: Date.init())

    let calendar = Calendar(identifier: .gregorian)
    var firstDayComponents = DateComponents()
    firstDayComponents.weekOfYear = weekOfYearWithYear.week
    firstDayComponents.yearForWeekOfYear = weekOfYearWithYear.year
    firstDayComponents.weekday = 1

    let firstDay = calendar.date(from: firstDayComponents)

    var lastDayComponents = DateComponents()
    lastDayComponents.weekOfYear = weekOfYearWithYear.week
    lastDayComponents.yearForWeekOfYear = weekOfYearWithYear.year
    lastDayComponents.weekday = 2

    let lastDay = calendar.date(from: lastDayComponents)
    output = (start: firstDay!, end: lastDay!)
    return output
}

.weekday = 2 -> leads to the sunday and not 0.

enter image description here

I also want to have the entire day and not 16:00.

PaFi
  • 888
  • 1
  • 9
  • 24
  • 1
    A `Date` is always printed in UTC – see https://stackoverflow.com/questions/39937019/nsdate-or-date-shows-the-wrong-time. You are probably in the GMT+8 timezone. – Martin R Mar 01 '19 at 05:56
  • okay makes sense, but do you have any idea why creating the date with the dayComponents does not really work? – PaFi Mar 01 '19 at 06:08

1 Answers1

2

A couple of observations:

  1. In the Gregorian calendar, weekday = 1 means Sunday; weekday = 2 means Monday; etc. You can look at calendar.maximumRange(of: .weekday) to get the range of valid values, and you can look at calendar.weekdaySymbols to see what these weekDay values mean (e.g. “Sun”, “Mon”, “Tue”, “Wed”, “Thu”, “Fri”, and “Sat”).

  2. You said:

    I also want to have the entire day and not 16:00.

    A Date object references a moment in time. So it can’t represent an “entire day”. But it can represent midnight (and midnight in your time zone is likely 4pm in GMT/UTC/Zulu).

    You can, alternatively, return a DateInterval, which does represent a range of time.

    func interval(ofWeek week: Int, in year: Int) -> DateInterval {
        let calendar = Calendar.current
    
        let date = DateComponents(calendar: calendar, weekOfYear: week, yearForWeekOfYear: year).date!
        return calendar.dateInterval(of: .weekOfYear, for: date)!
    }
    

    And then

    let formatter = DateIntervalFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .short
    
    let year = Calendar.current.component(.year, from: Date())
    let dateInterval = interval(ofWeek: 2, in: year)
    print(formatter.string(from: dateInterval))
    

    In a US locale, the interval starts on January 6th:

    1/6/19, 12:00 AM – 1/13/19, 12:00 AM

    Whereas in a German locale, the interval starts on the 7th:

    07.01.19, 00:00 – 14.01.19, 00:00

  3. If you want the start of the first day of the week and the last day of the week, you can do:

    func startAndEndDate(ofWeek week: Int, in year: Int) -> (Date, Date) {
        let date = DateComponents(calendar: calendar, weekOfYear: week, yearForWeekOfYear: year).date!
        let lastDate = calendar.date(byAdding: .day, value: 6, to: date)!
    
        return (date, lastDate)
    }
    

    And then

    let formatter = DateFormatter()
    formatter.dateStyle = .short
    
    let year = Calendar.current.component(.year, from: Date())
    let (start, end) = startAndEndDate(ofWeek: 2, in: year)
    print(formatter.string(from: start), "-", formatter.string(from: end))
    
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Setting `weekday = weekDayRange?.first (== 1)` does not take into account that a week starts on a Monday in some regions (like Germany). It might be better *not* to set that component for the first date, and compute the second date by adding one week minus one day. – Martin R Mar 01 '19 at 06:15
  • if I use the users current Calendar like Calendar.current, it does not handle the users region right? so basically I have to handle all the various calendar the user might use? Or what would be a more general solution? – PaFi Mar 01 '19 at 06:23
  • 1
    @MartinR - You’re right. I’ve updated my answer accordingly – Rob Mar 01 '19 at 07:19
  • @PaFi - No, I’d definitely suggest always using `Calendar.current`. Someone using a non-gregorian calendar will see very unusual results, if you don’t. Definitely test your code using non-gregorian calendars and different locales. – Rob Mar 01 '19 at 07:22
  • A German user will see "07.01.19, 00:00 – 14.01.19, 00:00". `Calendar` and `DateIntervalFormatter` have a `locale` property, the default value (probably) is the current locale which can be modified in the system prefs. – Willeke Mar 01 '19 at 09:40
  • @Willeke - Of course it will. I was focusing on Martin’s point, how the definition of of the week changes from locale to locale. But obviously, the formatting of the formatter’s resulting string also varies. – Rob Mar 01 '19 at 16:33