问题
I'm trying to get the dates within a given month.
My plan is to
- Get the start and the end dates of a given month,.
- Get all the dates that fall within that range.
- Iterate through them and eliminate dates that fall within a weekend using the isDateInWeekend method.
The remaining dates are the weekdays.
So I created two NSDate
extension methods to get the start and the end dates of the month.
extension NSDate {
var startOfMonth: NSDate {
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Year, .Month], fromDate: self)
return calendar.dateFromComponents(components)!
}
var endOfMonth: NSDate {
let calendar = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.month = 1
return (calendar.dateByAddingComponents(components, toDate: self.startOfMonth, options: NSCalendarOptions())?.dateByAddingTimeInterval(-1))!
}
}
Now I'm stuck at step #2. I can't find a way to get a range of dates given a start and an end date.
Is there a way to do this?
回答1:
Get the current calendar
let calendar = NSCalendar.currentCalendar()
Get the
month
andyear
date components from the current datelet components = calendar.components([.Year, .Month], fromDate: NSDate())
Get the date of the first day of the month
let startOfMonth = calendar.dateFromComponents(components)!
Get the number of days for the current month
let numberOfDays = calendar.rangeOfUnit(.Day, inUnit: .Month, forDate: startOfMonth).length
Create an array of
NSDate
instances for every day in the current monthlet allDays = Array(0..<numberOfDays).map{ calendar.dateByAddingUnit(.Day, value: $0, toDate: startOfMonth, options: [])!}
Filter the days within a weekend
let workDays = allDays.filter{ !calendar.isDateInWeekend($0) }
Swift 3:
let calendar = Calendar.current
let components = calendar.dateComponents([.year, .month], from: Date())
let startOfMonth = calendar.date(from:components)!
let numberOfDays = calendar.range(of: .day, in: .month, for: startOfMonth)!.upperBound
let allDays = Array(0..<numberOfDays).map{ calendar.date(byAdding:.day, value: $0, to: startOfMonth)!}
let workDays = allDays.filter{ !calendar.isDateInWeekend($0) }
回答2:
For a more general answer than the one you've given above, you could implement your own SequenceType
running over/generating NSDate
elements.
Modifying the SequenceType
DateRange
implementation from this blog post by Adam Preble (Swift < 2.0), we can construct the following:
/* Modified version of Adam Preble:s DateRange: http://adampreble.net/blog/2014/09/iterating-over-range-of-dates-swift/ */
func < (left: NSDate, right: NSDate) -> Bool {
return left.compare(right) == NSComparisonResult.OrderedAscending
}
struct DateRange : SequenceType {
var calendar: NSCalendar
var startDate: NSDate
var endDate: NSDate
var stepUnits: NSCalendarUnit
var stepValue: Int
func generate() -> Generator {
return Generator(range: self, firstDate: true)
}
struct Generator: GeneratorType {
var range: DateRange
var firstDate : Bool = true
mutating func next() -> NSDate? {
if firstDate {
firstDate = false
return range.startDate
}
guard let nextDate = range.calendar.dateByAddingUnit(range.stepUnits,
value: range.stepValue, toDate: range.startDate,
options: NSCalendarOptions.MatchFirst) where !(range.endDate < nextDate) else {
return nil
}
range.startDate = nextDate
return nextDate
}
}
}
With this, you can iterate over a range of NSDate
:s, just as you could over, say, a range of integers, without explicitly needing an array of NSDate
objects.
Example usage (making use of you startOfMonth()
and endOfMonth()
extensions for NSDate
):
/* Your NSDate extension */
extension NSDate {
// ... as in your question above
}
/* Example usage */
let formatter = NSDateFormatter()
formatter.dateFormat = "EEE, MMM. d, yyyy"
// print week days of current month, using DateRange (as well as your extension)
if let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) {
let dateRange = DateRange(calendar: calendar,
startDate: NSDate().startOfMonth,
endDate: NSDate().endOfMonth,
stepUnits: NSCalendarUnit.Day,
stepValue: 1)
for date in dateRange where !calendar.isDateInWeekend(date) {
print(formatter.stringFromDate(date))
}
}
Output:
/* Mon, Feb. 1, 2016
Tue, Feb. 2, 2016
Wed, Feb. 3, 2016
Thu, Feb. 4, 2016
Fri, Feb. 5, 2016
Mon, Feb. 8, 2016
Tue, Feb. 9, 2016
...
Thu, Feb. 25, 2016
Fri, Feb. 26, 2016
Mon, Feb. 29, 2016 */
A month worth of dates in an array ([NSDate]
) is probably not an issue, but using a sequence instead is valuable for larger date spans, and also when you need versatility, w.r.t. re-assigning size and members of an array; especially an array in which you really only use one member, in sequence, at a time.
E.g. using an NSDate
array in the example below would be quite un-necessary (and possibly yield unwanted overhead; but let's leave that as we want to avoid some commenter bringing up the subject of the sins of pre-mature optimization :) )
// versatile use of DateRange over somewhat "large" ranges of dates
formatter.dateFormat = "yyyy-MM-dd"
if let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian),
startDate = formatter.dateFromString("2016-01-01"),
endDate = formatter.dateFromString("2016-12-31") {
formatter.dateFormat = "EEE, MMM. d, yyyy"
// initialize dateRange instance
var dateRange = DateRange(calendar: calendar,
startDate: startDate,
endDate: endDate,
stepUnits: NSCalendarUnit.Day,
stepValue: 1)
// print all week days of 2016
for date in dateRange where !calendar.isDateInWeekend(date) {
print(formatter.stringFromDate(date))
}
print("")
// re-use same dateRange instance and print
// all week days of July 2016 -> June 2017
formatter.dateFormat = "yyyy-MM-dd"
if let startDate = formatter.dateFromString("2016-07-01"),
endDate = formatter.dateFromString("2017-06-30") {
// update dateRange instance
dateRange.startDate = startDate
dateRange.endDate = endDate
formatter.dateFormat = "EEE, MMM. d, yyyy"
for date in dateRange where calendar.isDateInWeekend(date) {
print(formatter.stringFromDate(date))
}
}
}
Output:
/* Mon, Feb. 1, 2016
Tue, Feb. 2, 2016
Wed, Feb. 3, 2016
Thu, Feb. 4, 2016
Fri, Feb. 5, 2016
Mon, Feb. 8, 2016
Tue, Feb. 9, 2016
...
Fri, Dec. 23, 2016
Mon, Dec. 26, 2016
Tue, Dec. 27, 2016
Wed, Dec. 28, 2016
Thu, Dec. 29, 2016
Fri, Dec. 30, 2016
// 2nd range print
Fri, Jul. 1, 2016
Mon, Jul. 4, 2016
Tue, Jul. 5, 2016
Wed, Jul. 6, 2016
Thu, Jul. 7, 2016
...
Fri, Jun. 23, 2017
Mon, Jun. 26, 2017
Tue, Jun. 27, 2017
Wed, Jun. 28, 2017
Thu, Jun. 29, 2017
Fri, Jun. 30, 2017 */
回答3:
With the help of this answer, I was able to accomplish this.
let calendar = NSCalendar.currentCalendar()
let normalizedStartDate = calendar.startOfDayForDate(NSDate().startOfMonth)
let normalizedEndDate = calendar.startOfDayForDate(NSDate().endOfMonth)
var dates = [normalizedStartDate]
var currentDate = normalizedStartDate
repeat {
currentDate = calendar.dateByAddingUnit(NSCalendarUnit.Day, value: 1, toDate: currentDate, options: .MatchNextTime)!
dates.append(currentDate)
} while !calendar.isDate(currentDate, inSameDayAsDate: normalizedEndDate)
let weekdays = dates.filter { !calendar.isDateInWeekend($0) }
weekdays.forEach { date in
print(NSDateFormatter.localizedStringFromDate(date, dateStyle: .FullStyle, timeStyle: .NoStyle))
}
And it works!
Monday, February 1, 2016
Tuesday, February 2, 2016
Wednesday, February 3, 2016
Thursday, February 4, 2016
Friday, February 5, 2016
Monday, February 8, 2016
Tuesday, February 9, 2016
Wednesday, February 10, 2016
Thursday, February 11, 2016
Friday, February 12, 2016
Monday, February 15, 2016
Tuesday, February 16, 2016
Wednesday, February 17, 2016
Thursday, February 18, 2016
Friday, February 19, 2016
Monday, February 22, 2016
Tuesday, February 23, 2016
Wednesday, February 24, 2016
Thursday, February 25, 2016
Friday, February 26, 2016
Monday, February 29, 2016
来源:https://stackoverflow.com/questions/35601583/get-weekdays-within-a-month