如何在Swift中获取本周星期一的日期

27
我试图获取当前周的星期一日期。在我的表格视图中,这被视为一周的第一天。 我还需要获取当前周的星期日。在我的表格视图中,这被视为一周的最后一天。

目前的尝试:

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

1
你可以使用日历ISO8601,其中第一个工作日是星期一,并且不将firstWeekday更改为1(星期日)let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)! - Leo Dabus
你的问题对我来说不够清楚。如果你设置 calendar.firstWeekday = 1,那么周日将被定义为一周的第一天,因此你得到的结果是可以预料的。 - Martin R
8个回答

94

我编写了日期扩展程序,以获取特定工作日的日期,以下是在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

这里是为此目的设计的日期扩展,

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

我需要更改什么才能在本周末(以本周为例,即11月1日)得到星期天? - user2363025
现在我可以使用get(.Previous, "Monday")来获取当前周的星期一,使用get(.Next, "Sunday", considerToday: true)可以成功获取当前周的星期日。如果这个星期从星期一开始,到星期日结束,那么这就是我需要的,但我还需要获取下一个星期的星期一和星期日。所以我可以使用你的get(.Next, "Monday")来获取下一周的星期一,但我无法获取下一周的星期日。这可行吗? - user2363025
要获取精确时间而不仅仅是12:00 AM,请查看我的答案。 - Muhammad Umair
1
如果我有2018-04-02并请求上一个星期日,它会给我2018-03-25而不是2018-04-01 - pableiros
@Sandeep,参考你的帖子,我可以问一个关于日期扩展的问题吗? - Newbie
显示剩余3条评论

39

您可以使用日历ISO8601,其中第一个工作日为星期一:

Swift 5.2或更高版本

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"

作为一个 Date 计算机属性扩展实现:

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"

2
完美答案,时区有小修正。 var comp: DateComponents = Calendar(identifier: .iso8601).dateComponents([.yearForWeekOfYear, .weekOfYear], from: Date()) comp.timeZone = TimeZone(secondsFromGMT: 0) let mondayDate = Calendar(identifier: .iso8601).date(from: comp)! print("星期一 \(mondayDate)") - Suresh Durishetti

22

这是Sandeep的答案的简化版本。

用法:

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

扩展:

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
}

如果“WeekDay”不以“Sunday”开头,那么它是否正确? - TheTiger
@TheTiger,它可以在不考虑系统偏好设置的情况下工作,但您不能更改枚举。 - John
嗨,@John,如何去除时间?我只需要显示日期。 - Amin Rezaew
@AminRezaew 你可以使用 DateFormatter 并将 timeStyle = .none - John

8

这是我创建的扩展程序,它首先找到星期日,然后加一天。

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

不错的扩展,但是.firstWeekday应该增加2吧?如果我使用值1,它会返回星期日的日期... return gregorian.date(byAdding: .day, value: 2, to: sunday) - Alessign
2
我不确定为什么你会得到星期日。我刚试了一下,得到的是星期一。http://swift.sandbox.bluemix.net/#/repl/598b2f8308bc28326a66e302 - Neorcisa
我认为你应该使用 gregorian.firstWeekday 而不是 1 或 2。 - ElOjcar
1
问题在于:如果今天是星期天,该扩展将返回明天的日期。如果你认为星期天是上周的最后一天,那么你应该得到上一个星期一,而不是下一个星期一。 - Slaknation

3

尝试使用:

calendar.firstWeekday = 2

编辑

更具体地说:NSCalendar.currentCalendar() 返回用户日历。根据文档:

返回的日历是由当前用户选择的系统语言环境设置和用户在“系统偏好设置”中指定的任何自定义设置重叠形成的。

如果你想让周一始终作为第一天,我认为你应该使用:

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

2

Swift 4 解决方案

根据我的需求,我已经找到了以下日期。

1. Today

2. Tomorrow 

3. This Week 

4. This Weekend 

5. Next Week 

6. Next Weekend

所以,我创建了一个名为Date Extension的扩展程序,用于获取本周下周的日期。

代码

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

使用方法:

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

您可以根据自己的需求修改扩展

谢谢!


1

增补@Saneep的回答

如果您想根据给定/当前日期获取确切的dateTime(比如说,您想要将星期一的dateTime转换为23-05-2016 12:00:0023-05-2016 05:35:17),请尝试以下方法:

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

-1

简单的代码(记得更好地处理可选项):

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)

1
并非每个日期都有24小时。 - Leo Dabus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接