first and last day of the current month in swift

IosSwiftNsdateNscalendar

Ios Problem Overview


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.

Ios Solutions


Solution 1 - Ios

Swift 3 and 4 drop-in extensions

This actually gets a lot easier with Swift 3+:

  1. 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).
  2. 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"

Solution 2 - Ios

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

Solution 3 - Ios

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.

Solution 4 - Ios

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

}

Solution 5 - Ios

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

Solution 6 - Ios

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

Solution 7 - Ios

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

Solution 8 - Ios

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

Solution 9 - Ios

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

Solution 10 - Ios

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
}

Solution 11 - Ios

/**
      Get last day of month..
 */
private func getLastDay(year:Int, month:Int) -> Int
{
    var ret:Int = -1
    
    switch month {
    case 1:
        ret = 31
        break
    case 2:
         break
    case 3:
        ret = 31
        break
    case 4:
        ret = 30
        break
    case 5:
        ret = 31
        break
    case 6:
        ret = 30
        break
    case 7:
        ret = 31
        break
    case 8:
        ret = 31
        break
    case 9:
        ret = 30
        break
    case 10:
        ret = 31
        break
    case 11:
        ret = 30
        break
    case 12:
        ret = 31
        break
    default:
        print("bad num")
    }
    
    if(month == 2)
    {
        ret = 28
        
        let t4 = year % 4
        let l100 = year % 100
        let l400 = year % 400
        
        if(t4 == 0 && l100 != 0)
        {
            ret = 29
        }
        
        if(l400 == 0)
        {
            ret = 29
        }
    }
    
    
    return ret
    
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2363025View Question on Stackoverflow
Solution 1 - IosbrandonscriptView Answer on Stackoverflow
Solution 2 - IosMartin RView Answer on Stackoverflow
Solution 3 - IosLuisCienView Answer on Stackoverflow
Solution 4 - IosBhadresh KathiriyaView Answer on Stackoverflow
Solution 5 - IosMiguel GallegoView Answer on Stackoverflow
Solution 6 - IosrubikView Answer on Stackoverflow
Solution 7 - IosFattieView Answer on Stackoverflow
Solution 8 - IosZulqarnain MustafaView Answer on Stackoverflow
Solution 9 - IosImanou PetitView Answer on Stackoverflow
Solution 10 - IosAlfredo Luco GView Answer on Stackoverflow
Solution 11 - IosBlazej KitaView Answer on Stackoverflow