How do I find the number of days in given month and year using swift - swift

I want to find the total number days on given month and year.
Example: I want to find total number of days on year = 2015, month = 7

First create an NSDate for the given year and month:
let dateComponents = NSDateComponents()
dateComponents.year = 2015
dateComponents.month = 7
let calendar = NSCalendar.currentCalendar()
let date = calendar.dateFromComponents(dateComponents)!
Then use the rangeOfUnit() method, as described in
Number of days in the current month using iPhone SDK?:
// Swift 2:
let range = calendar.rangeOfUnit(.Day, inUnit: .Month, forDate: date)
// Swift 1.2:
let range = calendar.rangeOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitMonth, forDate: date)
let numDays = range.length
print(numDays) // 31
Update for Swift 3 (Xcode 8):
let dateComponents = DateComponents(year: 2015, month: 7)
let calendar = Calendar.current
let date = calendar.date(from: dateComponents)!
let range = calendar.range(of: .day, in: .month, for: date)!
let numDays = range.count
print(numDays) // 31

Updated for Swift 3.1, Xcode 8+, iOS 10+
let calendar = Calendar.current
let date = Date()
// Calculate start and end of the current year (or month with `.month`):
let interval = calendar.dateInterval(of: .year, for: date)! //change year it will no of days in a year , change it to month it will give no of days in a current month
// Compute difference in days:
let days = calendar.dateComponents([.day], from: interval.start, to: interval.end).day!
print(days)

In extension format, using self to be able to return the number of days more dynamically (Swift 3).
extension Date {
func getDaysInMonth() -> Int{
let calendar = Calendar.current
let dateComponents = DateComponents(year: calendar.component(.year, from: self), month: calendar.component(.month, from: self))
let date = calendar.date(from: dateComponents)!
let range = calendar.range(of: .day, in: .month, for: date)!
let numDays = range.count
return numDays
}
}

Swift 5.0
func getDaysInMonth(month: Int, year: Int) -> Int? {
let calendar = Calendar.current
var startComps = DateComponents()
startComps.day = 1
startComps.month = month
startComps.year = year
var endComps = DateComponents()
endComps.day = 1
endComps.month = month == 12 ? 1 : month + 1
endComps.year = month == 12 ? year + 1 : year
let startDate = calendar.date(from: startComps)!
let endDate = calendar.date(from:endComps)!
let diff = calendar.dateComponents([Calendar.Component.day], from: startDate, to: endDate)
return diff.day
}
if let numberOfDays = getDaysInMonth(month: 1, year: 2015) {
print(numberOfDays)
}
Swift 2.0
func getDaysInMonth(month: Int, year: Int) -> Int
{
let calendar = NSCalendar.currentCalendar()
let startComps = NSDateComponents()
startComps.day = 1
startComps.month = month
startComps.year = year
let endComps = NSDateComponents()
endComps.day = 1
endComps.month = month == 12 ? 1 : month + 1
endComps.year = month == 12 ? year + 1 : year
let startDate = calendar.dateFromComponents(startComps)!
let endDate = calendar.dateFromComponents(endComps)!
let diff = calendar.components(NSCalendarUnit.Day, fromDate: startDate, toDate: endDate, options: NSCalendarOptions.MatchFirst)
return diff.day
}
let days = getDaysInMonth(4, year: 2015) // April 2015 has 30 days
print(days) // Prints 30
Swift 1.2
func getDaysInMonth(month: Int, year: Int) -> Int
{
let calendar = NSCalendar.currentCalendar()
let startComps = NSDateComponents()
startComps.day = 1
startComps.month = month
startComps.year = year
let endComps = NSDateComponents()
endComps.day = 1
endComps.month = month == 12 ? 1 : month + 1
endComps.year = month == 12 ? year + 1 : year
let startDate = calendar.dateFromComponents(startComps)!
let endDate = calendar.dateFromComponents(endComps)!
let diff = calendar.components(NSCalendarUnit.CalendarUnitDay, fromDate: startDate, toDate: endDate, options: NSCalendarOptions.allZeros)
return diff.day
}
let days = getDaysInMonth(4, 2015) // There were 30 days in April 2015
println(days) // Prints 30

In order to get number of days and all the dates on given month and year, try this.
func getAllDates(month: Int, year: Int) -> [Date] {
let dateComponents = DateComponents(year: year, month: month)
let calendar = Calendar.current
let date = calendar.date(from: dateComponents)!
let range = calendar.range(of: .day, in: .month, for: date)!
let numDays = range.count
let formatter = DateFormatter()
formatter.dateFormat = "yyyy MM dd"
formatter.timeZone = TimeZone(abbreviation: "GMT+0:00")
var arrDates = [Date]()
for day in 1...numDays {
let dateString = "\(year) \(month) \(day)"
if let date = formatter.date(from: dateString) {
arrDates.append(date)
}
}
return arrDates
}
Usage:
let arrDatesInGivenMonthYear = getAllDates(month: 1, year: 2018)
debugPrint(arrDatesInGivenMonthYear)
//Output: [2018-01-01 00:00:00 +0000, 2018-01-02 00:00:00 +0000, ... , 2018-01-31 00:00:00 +0000]
let numberOfDays = arrDatesInGivenMonthYear.count
debugPrint(numberOfDays)
//Output: 31

Swift 5
Another way to approach this:
extension Date {
func daysInMonth(_ monthNumber: Int? = nil, _ year: Int? = nil) -> Int {
var dateComponents = DateComponents()
dateComponents.year = year ?? Calendar.current.component(.year, from: self)
dateComponents.month = monthNumber ?? Calendar.current.component(.month, from: self)
if
let d = Calendar.current.date(from: dateComponents),
let interval = Calendar.current.dateInterval(of: .month, for: d),
let days = Calendar.current.dateComponents([.day], from: interval.start, to: interval.end).day
{ return days } else { return -1 }
}
}

here is the swift 4.0 version
func getTotalDate(){
// choose the month and year you want to look
var dateComponents = DateComponents()
dateComponents.year = 2018
dateComponents.month = 10
let calendar = Calendar.current
let datez = calendar.date(from: dateComponents)
// change .month into .year to see the days available in the year
let interval = calendar.dateInterval(of: .month, for: datez!)!
let days = calendar.dateComponents([.day], from: interval.start, to: interval.end).day!
print(days)
}

What about for a given year?
The following is printing the number of days in the date's month:
let range = userCalendar.range(of: .day, in: .year, for: Date())!
print("Days In Year: \(range.count)") // prints 31
... and not the number of days in the date's year as expected :/

Related

Date range betwen now and end of the day and next day and end of the next day

I'm trying to make a DatePicker with a choice between today and tomorrow. Two buttons to switch Date Range.
How to make today range? i. e. Now -> End of the day
How to make tomorrow range? i. e. Start of the next day -> End of the next day
what i've tried, but it's not correct
var todayRange: ClosedRange<Date> {
let calendar = Calendar.current
var endComponents = DateComponents()
endComponents.day = 1
let endDate = calendar.date(byAdding: endComponents, to: Date.now)!
return Date.now ... endDate
}
var tommorowRange: ClosedRange<Date> {
let calendar = Calendar.current
var startComponents = DateComponents()
startComponents.day = 2
var endComponents = DateComponents()
endComponents.day = 1
let startDate = calendar.date(byAdding: startComponents, to: Date.now)!
let endDate = calendar.date(byAdding: endComponents, to: startDate)!
return startDate ... endDate
}
You're close.
I would suggest:
Get the start of day for today
Add 1 day
Subtract 0.001 seconds.
That will give you the end of today.
Here is some sample code.
let today = Date.now
let startOfToday = Calendar.current.startOfDay(for: today)
let oneDay = DateComponents(day: 1)
guard let tomorrow = Calendar.current.date(byAdding: oneDay, to: startOfToday) else { fatalError() }
let endOfToday = tomorrow.advanced(by: -0.001)
let endOfTodayString = DateFormatter.localizedString(from: endOfToday, dateStyle: .medium, timeStyle: .medium)
print("End of today = " + endOfTodayString)
guard let endOfTomorrow = Calendar.current.date(byAdding: oneDay, to: endOfToday) else { fatalError() }
let endOfTomorrowString = DateFormatter.localizedString(from: endOfToday, dateStyle: .medium, timeStyle: .medium)
print("End of tomorrow = " + endOfTomorrowString)
In my locale (US), on October 7th, that generates:
End of today = Oct 7, 2022 at 11:59:59 PM
End of tomorrow = Oct 7, 2022 at 11:59:59 PM

How to round up date to midnight in Swift? [duplicate]

I'm trying to get the first and last day of the month in swift.
So far I have the following:
let dateFormatter = NSDateFormatter()
let date = NSDate()
dateFormatter.dateFormat = "yyyy-MM-dd"
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: date)
let month = components.month
let year = components.year
let startOfMonth = ("\(year)-\(month)-01")
But I'm not sure how to get the last date. Is there a built in method I'm missing? Obviously it has to take into account leap years etc.
Swift 3 and 4 drop-in extensions
This actually gets a lot easier with Swift 3+:
You can do it without guard (you could if you wanted to, but because DateComponents is a non-optional type now, it's no longer necessary).
Using iOS 8's startOfDayForDate (now startOfDay), you don't need to manually set the time to 12pm unless you're doing some really crazy calendar calculations across time zones.
It's worth mentioning that some of the other answers claim you can shortcut this by using Calendar.current.date(byAdding: .month, value: 0, to: Date())!, but where this fails, is that it doesn't actually zero out the day, or account for differences in timezones.
Here you go:
extension Date {
func startOfMonth() -> Date {
return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))!
}
func endOfMonth() -> Date {
return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())!
}
}
print(Date().startOfMonth()) // "2018-02-01 08:00:00 +0000\n"
print(Date().endOfMonth()) // "2018-02-28 08:00:00 +0000\n"
You get the first day of the month simply with
let components = calendar.components([.Year, .Month], fromDate: date)
let startOfMonth = calendar.dateFromComponents(components)!
print(dateFormatter.stringFromDate(startOfMonth)) // 2015-11-01
To get the last day of the month, add one month and subtract one day:
let comps2 = NSDateComponents()
comps2.month = 1
comps2.day = -1
let endOfMonth = calendar.dateByAddingComponents(comps2, toDate: startOfMonth, options: [])!
print(dateFormatter.stringFromDate(endOfMonth)) // 2015-11-30
Alternatively, use the rangeOfUnit method which gives you
the start and the length of the month:
var startOfMonth : NSDate?
var lengthOfMonth : NSTimeInterval = 0
calendar.rangeOfUnit(.Month, startDate: &startOfMonth, interval: &lengthOfMonth, forDate: date)
For a date on the last day of month, add the length of the month minus one second:
let endOfMonth = startOfMonth!.dateByAddingTimeInterval(lengthOfMonth - 1)
Updated for Swift5:
extension Date {
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
var startOfMonth: Date {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.year, .month], from: self)
return calendar.date(from: components)!
}
var endOfDay: Date {
var components = DateComponents()
components.day = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfDay)!
}
var endOfMonth: Date {
var components = DateComponents()
components.month = 1
components.second = -1
return Calendar(identifier: .gregorian).date(byAdding: components, to: startOfMonth)!
}
func isMonday() -> Bool {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.weekday], from: self)
return components.weekday == 2
}
}
With Swift 3 & iOS 10 the easiest way I found to do this is Calendar's dateInterval(of:for:):
guard let interval = calendar.dateInterval(of: .month, for: Date()) else { return }
You can then use interval.start and interval.end to get the dates you need.
Swift 3
Many date example for :
Last 6 month,
last 3 month,
yesterday, last 7 day, last 30 day, previous month,
current month start & end, last month start & end date
let startDate = dateFormatter.string(from: Date().getThisMonthStart()!)
let endDate = dateFormatter.string(from: Date().getThisMonthEnd()!)
extension Date {
func getLast6Month() -> Date? {
return Calendar.current.date(byAdding: .month, value: -6, to: self)
}
func getLast3Month() -> Date? {
return Calendar.current.date(byAdding: .month, value: -3, to: self)
}
func getYesterday() -> Date? {
return Calendar.current.date(byAdding: .day, value: -1, to: self)
}
func getLast7Day() -> Date? {
return Calendar.current.date(byAdding: .day, value: -7, to: self)
}
func getLast30Day() -> Date? {
return Calendar.current.date(byAdding: .day, value: -30, to: self)
}
func getPreviousMonth() -> Date? {
return Calendar.current.date(byAdding: .month, value: -1, to: self)
}
// This Month Start
func getThisMonthStart() -> Date? {
let components = Calendar.current.dateComponents([.year, .month], from: self)
return Calendar.current.date(from: components)!
}
func getThisMonthEnd() -> Date? {
let components:NSDateComponents = Calendar.current.dateComponents([.year, .month], from: self) as NSDateComponents
components.month += 1
components.day = 1
components.day -= 1
return Calendar.current.date(from: components as DateComponents)!
}
//Last Month Start
func getLastMonthStart() -> Date? {
let components:NSDateComponents = Calendar.current.dateComponents([.year, .month], from: self) as NSDateComponents
components.month -= 1
return Calendar.current.date(from: components as DateComponents)!
}
//Last Month End
func getLastMonthEnd() -> Date? {
let components:NSDateComponents = Calendar.current.dateComponents([.year, .month], from: self) as NSDateComponents
components.day = 1
components.day -= 1
return Calendar.current.date(from: components as DateComponents)!
}
}
Swift 4
If you only need the ordinal day:
func lastDay(ofMonth m: Int, year y: Int) -> Int {
let cal = Calendar.current
var comps = DateComponents(calendar: cal, year: y, month: m)
comps.setValue(m + 1, for: .month)
comps.setValue(0, for: .day)
let date = cal.date(from: comps)!
return cal.component(.day, from: date)
}
lastDay(ofMonth: 2, year: 2018) // 28
lastDay(ofMonth: 2, year: 2020) // 29
This is the simplest way that I found (Swift 5+):
extension Date {
func getStart(of component: Calendar.Component, calendar: Calendar = Calendar.current) -> Date? {
return calendar.dateInterval(of: component, for: self)?.start
}
func getEnd(of component: Calendar.Component, calendar: Calendar = Calendar.current) -> Date? {
return calendar.dateInterval(of: component, for: self)?.end
}
}
Here is easiest solution:
extension Date {
func startOfMonth() -> Date {
let interval = Calendar.current.dateInterval(of: .month, for: self)
return (interval?.start.toLocalTime())! // Without toLocalTime it give last months last date
}
func endOfMonth() -> Date {
let interval = Calendar.current.dateInterval(of: .month, for: self)
return interval!.end
}
// Convert UTC (or GMT) to local time
func toLocalTime() -> Date {
let timezone = TimeZone.current
let seconds = TimeInterval(timezone.secondsFromGMT(for: self))
return Date(timeInterval: seconds, since: self)
}}
and then call these with your date instance:
print(Date().startOfMonth())
print(Date().endOfMonth())
2017...
First, get the month you need:
let cal = Calendar.current
let d = Calendar.current.date(byAdding: .month, value: 0, to: Date())!
// for "last month" just use -1, for "next month" just use 1, etc
To get the day-of-the-week for the first day of the month:
let c = cal.dateComponents([.year, .month], from: d)
let FDOM = cal.date(from: c)!
let dowFDOM = cal.component(.weekday, from: FDOM)
print("the day-of-week on the 1st is ... \(dowFDOM)")
// so, that's 1=Sunday, 2=Monday, etc.
To get the number of days in the month:
let r = cal.range(of: .day, in: .month, for: d)!
let kDays = r.count
print("the number of days is ... \(kDays)")
With Swift 3, you can choose one of the two following patters in order to retrieve the first and last days of a month.
#1. Using Calendar dateComponents(_:from:), date(from:) and date(byAdding:to:wrappingComponents:) methods
With this pattern, you first get the date of the first day of a month then add a month and remove a day from it in order to get the date of the last day of the month. The Playground code below shows how to set it:
import Foundation
// Set calendar and date
let calendar = Calendar.current
let date = calendar.date(byAdding: DateComponents(day: -10), to: Date())!
// Get first day of month
let firstDayComponents = calendar.dateComponents([.year, .month], from: date)
let firstDay = calendar.date(from: firstDayComponents)!
// Get last day of month
let lastDayComponents = DateComponents(month: 1, day: -1)
let lastDay = calendar.date(byAdding: lastDayComponents, to: firstDay)!
// Set date formatter
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
// Print results
print(dateFormatter.string(from: date)) // Prints: 22 March 2017 at 18:07:15 CET
print(dateFormatter.string(from: firstDay)) // Prints: 1 March 2017 at 00:00:00 CET
print(dateFormatter.string(from: lastDay)) // Prints: 31 March 2017 at 00:00:00 CEST
#2. Using Calendar range(of:in:for:), dateComponents(_:from:) and date(from:) and methods
With this pattern, you get a range of absolute day values in a month and then retrieve the dates of the first day and last day of the month from it. The Playground code below shows how to set it:
import Foundation
// Set calendar and date
let calendar = Calendar.current
let date = calendar.date(byAdding: DateComponents(day: -10), to: Date())!
// Get range of days in month
let range = calendar.range(of: .day, in: .month, for: date)! // Range(1..<32)
// Get first day of month
var firstDayComponents = calendar.dateComponents([.year, .month], from: date)
firstDayComponents.day = range.lowerBound
let firstDay = calendar.date(from: firstDayComponents)!
// Get last day of month
var lastDayComponents = calendar.dateComponents([.year, .month], from: date)
lastDayComponents.day = range.upperBound - 1
//lastDayComponents.day = range.count // also works
let lastDay = calendar.date(from: lastDayComponents)!
// Set date formatter
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
// Print results
print(dateFormatter.string(from: date)) // prints: 22 March 2017 at 18:07:15 CET
print(dateFormatter.string(from: firstDay)) // prints: 1 March 2017 at 00:00:00 CET
print(dateFormatter.string(from: lastDay)) // prints: 31 March 2017 at 00:00:00 CEST
In swift 3, if you put 0 to day component you can get the last day of the month. There's an example code:
public func isMoreDays(date: Date, asc: Bool)->Bool{
//components
var dayComponents = self.getDateComponents(date: date)
//asc is true if ascendant or false if descendant
dayComponents.day = asc ? 0 : 1
//plus 1 to month 'cos if you set up day to 0 you are going to the previous month
dayComponents.month = asc ? dayComponents.month! + 1 : dayComponents.month
//instantiate calendar and get the date
let calendar : Calendar = NSCalendar.current
let day = calendar.date(from: dayComponents)
//date comparison
if(day?.compare(date) == .orderedSame){
return false
}
return true
}
You can use the following extensions here :
let today = Date()
let startOfMonth = today.beginning(of: .month)
let endOfMonth = today.end(of: .month)

How to have reoccurring countdown each year to a date in Swift

I am trying to incorporate a countdown to a holiday within my app in Swift and having trouble with the timer going into negative numbers once the date has passed. The countdown should reset after the date passing. What am I doing wrong?
var holidayDate: Date {
let currentYear = Date()
let userCalendar = Calendar.current
var components = DateComponents()
components.year = userCalendar.component(.year, from: currentYear)
components.day = 02
components.month = 11
return userCalendar.date(from: components)!
}
var today: Date {
let now = Date()
let userCalendar = Calendar.current
var components = DateComponents()
components.year = userCalendar.component(.year, from: now)
components.day = userCalendar.component(.day, from: now)
components.month = userCalendar.component(.month, from: now)
return userCalendar.date(from: components)!
}
func daysBetweenDates(startDate: Date, endDate: Date) -> Int {
let calendar = Calendar.current
let components = calendar.dateComponents([.day], from: startDate, to: endDate)
return components.day!
}
You can simply use calendar nextDate method to get the next date matching components and pass the month and day components to it:
func daysUntilNextDate(matching components: DateComponents) -> Int {
let date = Date()
guard let calendar = components.calendar,
let nextDate = calendar.nextDate(after: date, matching: components, matchingPolicy: .strict) else { return .zero }
return calendar.dateComponents([.day], from: date, to: nextDate).day!
}
let holiday: DateComponents = .init(calendar: .current, month: 11, day: 2)
let christmas: DateComponents = .init(calendar: .current, month: 12, day: 25)
daysUntilNextDate(matching: holiday) // 363
daysUntilNextDate(matching: christmas) // 51

How to calculate the difference in days for two Date in Swift as days start at 6 a.m. instead of 0

I'm trying to get the difference in two Date objects.
I know how to do it basically, which is like this:
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale.current
print(count)
// print(history[count - 1].value(forKey: "multipleURLs") as! [String])
let latestDate = history[count - 1].value(forKey: "date") as! Date
let calendar = Calendar.current
let components = calendar.dateComponents([.year, .month, .day, .hour], from: date)
let nowDate = calendar.startOfDay(for: date)
let lastDate = calendar.startOfDay(for: latestDate)
let diffDateComponent = (calendar as NSCalendar).components([NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.day], from: lastDate, to: nowDate, options: NSCalendar.Options.init(rawValue: 0))
print("Status Checked" + String(describing: diffDateComponent.day))
But I'm trying to get the result as a day start from 6 a.m. in the morning instead of 0.
So I did something like this:
let nowDate = calendar.startOfDay(for: date.addingTimeInterval(-3600))
let lastDate = calendar.startOfDay(for: latestDate.addingTimeInterval(-3600))
But it doesn't seem to be working, can anyone help me with this?
This will set each date to 6 AM (in the current locale of the Date):
import Foundation
// set up dates
let calendar = Calendar.current
let date = Date()
if let latestDate = calendar.date(byAdding: .day, value: -5, to: date) {
// set each day to 6 AM
if let nowDate = calendar.date(bySettingHour: 6, minute: 0, second: 0, of: date ),
let lastDate = calendar.date(bySettingHour: 6, minute: 0, second: 0, of: latestDate) {
print( date.description(with: .current),
nowDate.description(with: .current),
lastDate.description(with: .current),
separator: "\n")
// calculate difference in days
if let days = calendar.dateComponents([.day], from: lastDate, to: nowDate).day {
print("Days since: \(days)")
}
}
}
// Thursday, June 8, 2017 at 7:59:42 AM Eastern Daylight Time
// Thursday, June 8, 2017 at 6:00:00 AM Eastern Daylight Time
// Saturday, June 3, 2017 at 6:00:00 AM Eastern Daylight Time
// Days since: 5
Thanks guy, I finally figured it out. In case anyone need it, this is how it's done:
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale.current
let latestDate = history[count - 1].value(forKey: "date") as! Date
let calendar = Calendar.current
let nowDate = calendar.date(byAdding: .hour, value: -6, to: date)
let lastDate = calendar.date(byAdding: .hour, value: -6, to: latestDate)
let finalNowDate = calendar.startOfDay(for: nowDate!)
let finalLastDate = calendar.startOfDay(for: lastDate!)
let diffDateComponent = calendar.dateComponents([.day], from: finalLastDate, to: finalNowDate)
print("dif\(diffDateComponent.day)")

Calculate number of weeks in a given month - Swift

Having looked at a few different suggestions on SO, I've not been able to determine why the function below does not work. It seems to return 6 for some months and 5 for others. When changing weeks for days it works perfectly.
For example, trying
weeksInMonth(8, forYear 2015)
Results in 6.
I believe I have a mis-understanding of what 'firstWeekday' property on calendar does but haven't found an adequate explanation by Apple or online.
Have tried both .WeekOfMonth and .WeekOfYear. Again can't find explanation of exact difference.
Any suggestions would be greatly welcome.
func weeksInMonth(month: Int, forYear year: Int) -> Int?
{
if (month < 1 || month > 12) { return nil }
let dateString = String(format: "%4d/%d/01", year, month)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy/MM/dd"
if let date = dateFormatter.dateFromString(dateString),
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
{
calendar.firstWeekday = 2 // Monday
let weekRange = calendar.rangeOfUnit(.WeekOfMonth, inUnit: .Month, forDate: date)
let weeksCount = weekRange.length
return weeksCount
}
else
{
return nil
}
}
Update:
Apologies my question was not clear. I'm trying to work out how many weeks there are in a month that include a Monday in them. For August this should be 5.
Your code computes the number of weeks which occur (complete or partially)
in a month. What you apparently want is the number of Mondays
in the given month. With NSDateComponents and in particular with
the weekdayOrdinal property you can compute the first
(weekdayOrdinal=1) and last (weekdayOrdinal=-1) Monday
in a month. Then compute the difference in weeks (and add one).
A possible implementation in Swift 2:
func numberOfMondaysInMonth(month: Int, forYear year: Int) -> Int?
{
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
calendar.firstWeekday = 2 // 2 == Monday
// First monday in month:
let comps = NSDateComponents()
comps.month = month
comps.year = year
comps.weekday = calendar.firstWeekday
comps.weekdayOrdinal = 1
guard let first = calendar.dateFromComponents(comps) else {
return nil
}
// Last monday in month:
comps.weekdayOrdinal = -1
guard let last = calendar.dateFromComponents(comps) else {
return nil
}
// Difference in weeks:
let weeks = calendar.components(.WeekOfMonth, fromDate: first, toDate: last, options: [])
return weeks.weekOfMonth + 1
}
Note: That a negative weekdayOrdinal counts backwards from the end of the month is not apparent form the documentation. It was observed in
Determine NSDate for Memorial Day in a given year and confirmed by Dave DeLong).
Update for Swift 3:
func numberOfMondaysInMonth(_ month: Int, forYear year: Int) -> Int? {
var calendar = Calendar(identifier: .gregorian)
calendar.firstWeekday = 2 // 2 == Monday
// First monday in month:
var comps = DateComponents(year: year, month: month,
weekday: calendar.firstWeekday, weekdayOrdinal: 1)
guard let first = calendar.date(from: comps) else {
return nil
}
// Last monday in month:
comps.weekdayOrdinal = -1
guard let last = calendar.date(from: comps) else {
return nil
}
// Difference in weeks:
let weeks = calendar.dateComponents([.weekOfMonth], from: first, to: last)
return weeks.weekOfMonth! + 1
}
Actually your question is : How many Mondays are in a given month?
My approach is to calculate the first Monday for the month, this can be accomplished by setting the CalendarUnit WeekdayOrdinal to 1. Then get the number of total days and do some math.
Swift 1.2
func mondaysInMonth(month: Int, forYear year: Int) -> Int?
{
if 1...12 ~= month {
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = NSDateComponents()
components.weekday = 2 // Monday
components.weekdayOrdinal = 1
components.month = month
components.year = year
if let date = calendar.dateFromComponents(components) {
let firstDay = calendar.component(.CalendarUnitDay, fromDate: date)
let days = calendar.rangeOfUnit(.CalendarUnitDay, inUnit:.CalendarUnitMonth, forDate:date).length
return (days - firstDay) / 7 + 1
}
}
return nil
}
Swift 2
func mondaysInMonth(month: Int, forYear year: Int) -> Int?
{
guard 1...12 ~= month else { return nil }
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = NSDateComponents()
components.weekday = 2 // Monday
components.weekdayOrdinal = 1
components.month = month
components.year = year
if let date = calendar.dateFromComponents(components) {
let firstDay = calendar.component(.Day, fromDate: date)
let days = calendar.rangeOfUnit(.Day, inUnit:.Month, forDate:date).length
return (days - firstDay) / 7 + 1
}
return nil
}
Swift 2
The following code calculates the number of weeks in a month. It does not depend on what day the month started or ended.
func weeksInMonth(month: Int, year: Int) -> (Int)? {
let calendar = NSCalendar.currentCalendar()
let comps = NSDateComponents()
comps.month = month+1
comps.year = year
comps.day = 0
guard let last = calendar.dateFromComponents(comps) else {
return nil
}
// Note: Could get other options as well
let tag = calendar.components([.WeekOfMonth,.WeekOfYear,
.YearForWeekOfYear,.Weekday,.Quarter],fromDate: last)
return tag.weekOfMonth
}
// Example Usage:
if let numWeeks = weeksInMonth(8,year: 2015) {
print(numWeeks) // Prints 6
}
if let numWeeks = weeksInMonth(12,year: 2015) {
print(numWeeks) // Prints 5
}
Swift 4+:
//-- Get number of weeks from calendar
func numberOfWeeksInMonth(_ date: Date) -> Int {
var calendar = Calendar(identifier: .gregorian)
calendar.firstWeekday = 1
let weekRange = calendar.range(of: .weekOfMonth,
in: .month,
for: date)
return weekRange!.count
}
//-- Implementation
let weekCount = numberOfWeeksInMonth(Date)
Swift 4+:
Generate Date from your components:
let dateComponents = DateComponents.init(year: 2019, month: 5)
let monthCurrentDayFirst = Calendar.current.date(from: dateComponents)!
let monthNextDayFirst = Calendar.current.date(byAdding: .month, value: 1, to: monthCurrentDayFirst)!
let monthCurrentDayLast = Calendar.current.date(byAdding: .day, value: -1, to: monthNextDayFirst)!
Detect week number from date:
let numberOfWeeks = Calendar.current.component(.weekOfMonth, from: monthCurrentDayLast)