Swift 如何从NSDate开始查找一周的开始?

Swift 如何从NSDate开始查找一周的开始?,swift,date,calendar,Swift,Date,Calendar,我正在实现一个日历视图,我希望它从包含特定日期的周初开始。如果目标日期是2016年2月29日星期一,当前日历设置为周日开始,我希望我的视图从2月28日周日开始 这似乎应该是直截了当的: let calendar = NSCalendar.currentCalendar() let firstDate = calendar.nextDateAfterDate(targetDate, matchingUnit: .Weekday,

我正在实现一个日历视图,我希望它从包含特定日期的周初开始。如果目标日期是2016年2月29日星期一,当前日历设置为周日开始,我希望我的视图从2月28日周日开始

这似乎应该是直截了当的:

let calendar = NSCalendar.currentCalendar()
let firstDate = calendar.nextDateAfterDate(targetDate, 
                    matchingUnit: .Weekday,
                           value: calendar.firstWeekday,
                         options: .SearchBackwards)
但这在以下方面失败:

由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“必须指定集合{NSCalendarMatchPreviousTimePreservingSmallerUnits,NSCalendarMatchNextTimePreservingSmallerUnits,NSCalendarMatchNextTime}中的一个选项。”

我基本上可以通过以下方式获得我想要的:

let firstDate = calendar.nextDateAfterDate(firstDate, 
                    matchingUnit: .Weekday,
                           value: calendar.firstWeekday,
                        options: .MatchPreviousTimePreservingSmallerUnits)?
                    .dateByAddingTimeInterval(-7 * 84600)
但这似乎是一个糟糕的做法,因为有时一天的秒数不是86400

有更好的方法吗?

基本上使用

压片机

dateByAddingComponents

。要解决您的问题,请尝试使用以下代码示例:

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) {
         print(beginningOfWeek) 
    }
}

您可以使用
Calendar
方法
date(from:DateComponents)
从任何日期传递
[.yearForWeekOfYear.weekOfYear]
组件它将返回所用日历中一周的第一天。所以,如果你想得到周日,就用公历吧。如果您想将周一作为一周的第一天,可以使用Calendar
.iso8601
,如图所示

Xcode 12•Swift 5.3或更高版本(也适用于以前的Swift版本)



用法:

Date().startOfWeek()  // "Sep 20, 2020 at 12:00 AM"


如果您希望在特定时区获得一周的开始时间,您只需要使用自定义日历:

var gregorianUTC = Calendar.gregorian
gregorianUTC.timeZone = TimeZone(identifier: "UTC")!
print(Date().startOfWeek(using: gregorianUTC))  // "2020-09-20 00:00:00 +0000\n"

日历有一种机制,用于在包含给定日期的给定时间间隔(如一年中的一周或一个月)的开始处查找日期:

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = dateFormatter.date(from: "2017-01-07")

if let date = date {
    let calendar = Calendar(identifier: .gregorian)

    var startDate : Date = Date()
    var interval : TimeInterval = 0

    if calendar.dateInterval(of: .weekOfYear, start: &startDate, interval: &interval, for: date) {
        print("Start of week is \(startDate)")
        // prints "Start of week is 2017-01-01 06:00:00 +0000"
    }
}

Swift 4解决方案

我已经根据我的要求计算出了,我已经找到了下面的日期

1. Today

2. Tomorrow 

3. This Week 

4. This Weekend 

5. Next Week 

6. Next Weekend
因此,我创建了
日期扩展
,以获取本周和下周的日期

代码

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)
    }
}
您可以根据需要修改扩展名


谢谢

我以前的所有解决方案都有问题,因为它们没有考虑用户的日历设置。下一个代码将考虑到这一点

extension Date {    

    var startOfWeek: Date? {
        let calendar = Calendar.current
        var components: DateComponents? = calendar.dateComponents([.weekday, .year, .month, .day], from: self)
        var modifiedComponent = components
        modifiedComponent?.day = (components?.day ?? 0) - ((components?.weekday ?? 0) - 1)

        return calendar.date(from: modifiedComponent!)
    }

    var endOfWeek: Date? {
        let calendar = Calendar.current
        var components: DateComponents? = calendar.dateComponents([.weekday, .year, .month, .day], from: self)
        var modifiedComponent = components
        modifiedComponent?.day = (components?.day ?? 0) + (7 - (components?.weekday ?? 0))
        modifiedComponent?.hour = 23
        modifiedComponent?.minute = 59
        modifiedComponent?.second = 59

        return calendar.date(from: modifiedComponent!)

    }
}

您可以将其实现为日期类扩展或其他内容。它应该返回类似于
2020-01-06 00:00:00+0000的值

Xcode 11.3 Swift 5

func firstDayOfWeek() -> Date {
    var c = Calendar(identifier: .iso8601)
    c.timeZone = TimeZone(secondsFromGMT: 0)!
    print(
        c.date(from: c.dateComponents([.weekOfYear, .yearForWeekOfYear], from: Date()))!
    )
} 

谢谢这是一个优雅的方式,这是一个很酷的方式,但它有一个小问题,夏令时。这可以通过以下代码修复:
var startOfWeek:NSDate{let date=Calendar.gregorian.datefrom组件(Calendar.gregorian.components([.YearForWeekOfYear.WeekOfYear],fromDate:self));let dsltimecoffset=NSTimeZone.local.daylightsavingtimecoffset(for:date);return date.addingTimeInterval(dsltimecoffset);}
最好使用NSCalendar.autoupdateingcurrent而不是gregorian您必须使用Calendar.current而不是Calendar(标识符:.gregorian)可能不是,但我更喜欢将其保留为可选,并使用guard作为常用的hanks!虽然我用另一种方法来解决这个问题,但这个答案帮助我更好地理解了如何使用DATEYBADION组件。别忘了你可以设定你想考虑哪一天是第一天使用:var GRGGORION =日历(标识符:GRGORION)gregorian.firstWeekday=2//monday这将返回UTC时间而非本地时间的一周开始时间。
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
extension Date {    

    var startOfWeek: Date? {
        let calendar = Calendar.current
        var components: DateComponents? = calendar.dateComponents([.weekday, .year, .month, .day], from: self)
        var modifiedComponent = components
        modifiedComponent?.day = (components?.day ?? 0) - ((components?.weekday ?? 0) - 1)

        return calendar.date(from: modifiedComponent!)
    }

    var endOfWeek: Date? {
        let calendar = Calendar.current
        var components: DateComponents? = calendar.dateComponents([.weekday, .year, .month, .day], from: self)
        var modifiedComponent = components
        modifiedComponent?.day = (components?.day ?? 0) + (7 - (components?.weekday ?? 0))
        modifiedComponent?.hour = 23
        modifiedComponent?.minute = 59
        modifiedComponent?.second = 59

        return calendar.date(from: modifiedComponent!)

    }
}
func firstDayOfWeek() -> Date {
    var c = Calendar(identifier: .iso8601)
    c.timeZone = TimeZone(secondsFromGMT: 0)!
    print(
        c.date(from: c.dateComponents([.weekOfYear, .yearForWeekOfYear], from: Date()))!
    )
}