10

I'm trying to get the start and end dates of the current month in dd/MM/yyyy format. I tried using extension as answered in this SO Question.But it seems like it's not what I want(the format is different and also it's giving me last month's last date and current month last but one date ). Can some one help me.

Extension Class:

extension Date {
    func startOfMonth() -> Date? {
        let comp: DateComponents = Calendar.current.dateComponents([.year, .month, .hour], from: Calendar.current.startOfDay(for: self))
        return Calendar.current.date(from: comp)!
    }

    func endOfMonth() -> Date? {
        var comp: DateComponents = Calendar.current.dateComponents([.month, .day, .hour], from: Calendar.current.startOfDay(for: self))
        comp.month = 1
        comp.day = -1
        return Calendar.current.date(byAdding: comp, to: self.startOfMonth()!)
    }
}

My Struct:

struct Constants{

    // keys required for making a Login call (POST Method)
    struct LoginKeys {
       .....
    }

    struct RankingKeys {

        static let DateFrom = String(describing: Date().startOfMonth()) //giving me 2016-11-30 16:00:00 +0000 
        static let DateTo  = String(describing: Date().endOfMonth())
//2016-12-30 16:00:00 +0000
    }
}

Expected Result:

DateFrom  = "01/12/2016"
DateTo = "31/12/2016"
Community
  • 1
  • 1
ASN
  • 1,655
  • 4
  • 24
  • 52
  • May be time-zone issue. – Bista Dec 19 '16 at 05:50
  • Even the dates are wrong. start date should first day of December and end day should be 31st but the outputs are different – ASN Dec 19 '16 at 05:53
  • 1
    Your code is fine. You are getting the correct values in local time. Please learn how to read the output of logging a `Date` object. Note the timezone in the output. – rmaddy Dec 19 '16 at 05:54
  • 1
    @ASN I have function of getting last date of month, If you want? I will try to get first date of month as well – Jitendra Modi Dec 19 '16 at 06:17
  • To clarify, your calculations are correct. You just need to properly convert `Date` to `String` using `DateFormatter`. – rmaddy Dec 19 '16 at 06:18
  • @Jecky that would be helpful too. But `rmaddy` says this code is also correct. Only thing is I have to read the output and display it as per my requirement – ASN Dec 19 '16 at 06:19
  • @ASN Do you want only string from that function as per your expected result ? – Jitendra Modi Dec 19 '16 at 06:41
  • @Jecky I'm getting the output but wrong first and last dates and the format is also not as my requirement. So I'm trying to look into the code for getting correct dates and then want them as string output – ASN Dec 19 '16 at 06:47
  • @ASN I already check your extension that works completely, You have to just convert that date according to timezone. I have checked your extension in my project and convert that date successfully but into swift 2.2, If you want then I will post my answer – Jitendra Modi Dec 19 '16 at 06:51
  • @ASN check answer – Jitendra Modi Dec 19 '16 at 06:53

6 Answers6

15

You should write this simple code:

let dateFormatter = DateFormatter()
let date = Date()
dateFormatter.dateFormat = "dd-MM-yyyy"

For start Date:

let comp: DateComponents = Calendar.current.dateComponents([.year, .month], from: date)
let startOfMonth = Calendar.current.date(from: comp)!
print(dateFormatter.string(from: startOfMonth))

For end Date:

var comps2 = DateComponents()
comps2.month = 1
comps2.day = -1
let endOfMonth = Calendar.current.date(byAdding: comps2, to: startOfMonth)
print(dateFormatter.string(from: endOfMonth!)) 
Pragnesh Vitthani
  • 2,532
  • 20
  • 28
  • 3
    This method gives one day less than the actual start date. E.g., For the Date 2020-09-16, it should give 2020-09-01 but instead it gives 2020-08-31 – zulkarnain shah Sep 16 '20 at 08:08
  • maybe this is caused by the print iso output so maybe some hours different from where you live – so this is intentional.(timezones) Try to output it in a formatted way, should be all right. – gustav Jul 02 '21 at 12:36
11

This is what I'm using. Pretty simple but it works.

extension Calendar {

func dayOfWeek(_ date: Date) -> Int {
    var dayOfWeek = self.component(.weekday, from: date) + 1 - self.firstWeekday

    if dayOfWeek <= 0 {
        dayOfWeek += 7
    }

    return dayOfWeek
}

func startOfWeek(_ date: Date) -> Date {
    return self.date(byAdding: DateComponents(day: -self.dayOfWeek(date) + 1), to: date)!
}

func endOfWeek(_ date: Date) -> Date {
    return self.date(byAdding: DateComponents(day: 6), to: self.startOfWeek(date))!
}

func startOfMonth(_ date: Date) -> Date {
    return self.date(from: self.dateComponents([.year, .month], from: date))!
}

func endOfMonth(_ date: Date) -> Date {
    return self.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth(date))!
}

func startOfQuarter(_ date: Date) -> Date {
    let quarter = (self.component(.month, from: date) - 1) / 3 + 1
    return self.date(from: DateComponents(year: self.component(.year, from: date), month: (quarter - 1) * 3 + 1))!
}

func endOfQuarter(_ date: Date) -> Date {
    return self.date(byAdding: DateComponents(month: 3, day: -1), to: self.startOfQuarter(date))!
}

func startOfYear(_ date: Date) -> Date {
    return self.date(from: self.dateComponents([.year], from: date))!
}

func endOfYear(_ date: Date) -> Date {
    return self.date(from: DateComponents(year: self.component(.year, from: date), month: 12, day: 31))!
}
}

How to use

let calendar: Calendar = Calendar.current
let startDate = calendar.startOfMonth(Date())
print("startDate :: \(startDate)")
Hardik Thakkar
  • 15,269
  • 2
  • 94
  • 81
Hung Hoang
  • 697
  • 5
  • 14
8

Here is an easy solution in create an extension for Date like following:

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 with your Date instance like that

print(Date().startOfMonth())
print(Date().endOfMonth())
Zulqarnain Mustafa
  • 1,615
  • 2
  • 22
  • 25
3

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 }

Then use a date formatter to print the dates:

let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
let dateText = formatter.string(from: interval.start)
LuisCien
  • 6,362
  • 4
  • 34
  • 42
0

This Extension Gives you expected output as per you want

Here I return date

extension NSDate {
    func startOfMonth() -> NSDate? {
        guard
            let cal: NSCalendar = NSCalendar.currentCalendar(),
            let comp: NSDateComponents = cal.components([.Year, .Month], fromDate: self) else { return nil }
        comp.to12pm()
        let dateformattor = NSDateFormatter()
        dateformattor.dateFormat = "yyyy-MM-dd"
        dateformattor.timeZone = NSTimeZone.localTimeZone()
        let dt2 = dateformattor.stringFromDate(cal.dateFromComponents(comp)!)
        print(dt2)

        dateformattor.dateFormat = "yyyy-MM-dd"
        dateformattor.timeZone = NSTimeZone.init(abbreviation: "UTC")

        return dateformattor.dateFromString(dt2)
    }

    func endOfMonth() -> NSDate? {
        guard
            let cal: NSCalendar = NSCalendar.currentCalendar(),
            let comp: NSDateComponents = NSDateComponents() else { return nil }
        comp.month = 1
        comp.day = -1
        comp.to12pm()
        let dateformattor = NSDateFormatter()
        dateformattor.dateFormat = "yyyy-MM-dd"
        dateformattor.timeZone = NSTimeZone.localTimeZone()
        let dt2 = dateformattor.stringFromDate(cal.dateByAddingComponents(comp, toDate: self.startOfMonth()!, options: [])!)

        dateformattor.dateFormat = "yyyy-MM-dd"
        dateformattor.timeZone = NSTimeZone.init(abbreviation: "UTC")

        return dateformattor.dateFromString(dt2)
    }
}
internal extension NSDateComponents {
    func to12pm() {
        self.hour = 12
        self.minute = 0
        self.second = 0
    }
}

**OUTPUT :- **

Start Date of Month :- 2016-12-01 00:00:00 +0000
End Date of Month :- 2016-12-31 00:00:00 +0000
Jitendra Modi
  • 2,344
  • 12
  • 34
  • The question was how to do this in Swift 3. This is a Swift 2 answer. – Rob Dec 19 '16 at 12:43
  • @Rob I ask the questioner for posting answer in swift 2.2 and after his permission I post it – Jitendra Modi Dec 19 '16 at 12:49
  • There are so many things wrong with this code. Why do you set the components to 12pm before creating a date? Date formatter defaults to local time so there's no need to explicitly set it to local time. Why convert the date to a string and then back to a date in UTC? Most of the posted code in unnecessary. – rmaddy Dec 19 '16 at 16:19
0

For the sake of completeness, the API dateInterval(of:start:interval:for:) of Calendar assigns the start date and interval (in seconds) of the current month to the inout parameters.

The date formatter considers the current time zone.

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "dd/MM/yyyy"

var startDate = Date()
var interval = TimeInterval()
Calendar.current.dateInterval(of: .month, start: &startDate, interval: &interval, for: Date())
let endDate = Calendar.current.date(byAdding: .second, value: Int(interval) - 1, to: startDate)!

let fromDate = formatter.string(from: startDate)
let toDate = formatter.string(from: endDate)
print(fromDate, toDate)
vadian
  • 274,689
  • 30
  • 353
  • 361