295

How can I get the year/month/day of a NSDate object, given no other information? I realize that I could probably do this with something similar to this:

NSCalendar *cal = [[NSCalendar alloc] init];
NSDateComponents *components = [cal components:0 fromDate:date];
int year = [components year];
int month = [components month];
int day = [components day];

But that seems to be a whole lot of hassle for something as simple as getting a NSDate's year/month/day. Is there any other solution?

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • 4
    I had a bit of trouble using this code until I changed the first line to "NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];" There must have been a change to the API since this question was asked. – Russell Thackston Apr 30 '12 at 11:48
  • @futureelite7 rolled back to revision 1. The code there is for historical purposes, and was a simple explanation of what my initial idea was. – Richard J. Ross III Jul 03 '12 at 04:22
  • The code no longer works. Can you make a note to the correct code? – futureelite7 Jul 03 '12 at 04:49
  • @futureelite7 no. That code was a showing of my first attempt, it was never intended to work. If you feel that that is ambiguous, feel free to edit the context surrounding that code block, but don't edit the code itself. If you feel extremely strongly about editing the code itself, then please take this coversation to meta and we'll see what other mods have to say. – Richard J. Ross III Jul 03 '12 at 05:04
  • Agreed.. this code doesn't work. It just sets year, month and day to 0. – Mike Gledhill May 16 '13 at 09:28
  • 9
    @mike thats why you read the answers, not the question. – Richard J. Ross III May 16 '13 at 12:17

17 Answers17

620

Because this is apparently my most popular answer, I'll try to edit it to contain a little bit more information.

Despite its name, NSDate in and of itself simply marks a point in machine time, not a date. There's no correlation between the point in time specified by an NSDate and a year, month, or day. For that, you have to refer to a calendar. Any given point in time will return different date information based on what calendar you're looking at (dates are not the same in both the Gregorian and Jewish calendars, for instance), and while the Gregorian calendar is the most widely used calendar in the world - I'm assuming - we're a little biased that NSDate should always use it. NSDate, luckily, is far more bipartisan.


Getting date and time is going to have to pass through NSCalendar, as you mentioned, but there's a simpler way to do it:

NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];

That generates an NSDateComponents object containing the day, month, and year from the current system calendar for the current day. (Note: this isn't necessarily the current user-specified calendar, just the default system one.)

Of course, if you're using a different calendar or date, you can easily change that. A list of available calendars and calendar units can be found in the NSCalendar Class Reference. More information about NSDateComponents can be found in the NSDateComponents Class Reference.


For reference, accessing individual components from the NSDateComponents is rather easy:

NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];

You just have to be mindful: NSDateComponents won't contain valid information for any fields you ask for unless you generated them with that valid information (i.e. request NSCalendar to provide that information with NSCalendarUnits). NSDateComponents contain no reference information in and of themselves - they're just simple structures that hold numbers for you to access. If you want to also get an era, for instance, out of NSDateComponents, you'll have to feed the generator method from NSCalendar with the NSCalendarUnitEra flag.

Christian Schnorr
  • 10,768
  • 8
  • 48
  • 83
Itai Ferber
  • 28,308
  • 5
  • 77
  • 83
  • 3
    A correction to this, the first line should have NSDayCalendarUnit instead of NSWeekCalendarUnit. – whitehawk Sep 22 '10 at 00:16
  • @Erik Which calendar are you using? Code? – Itai Ferber May 03 '12 at 04:40
  • I removed my comment because it was ignorant. ;) It was showing the 29th becuase 2012-04-30 GMT is five hours earlier here in Texas... Sorry, your code is fine. – Erik May 03 '12 at 19:29
  • @Erik No worries — I had a feeling something like that was the case. :) – Itai Ferber May 03 '12 at 21:10
  • Be careful [NSCalendar currentCalendar] is a very expensive method each time executed – Pascalius Aug 15 '12 at 15:10
  • @Pascalius Asking just out of curiosity: expensive compared to what? And what makes it especially expensive? – Itai Ferber Aug 15 '12 at 16:30
  • @ItaiFerber [NSCalendar currentCalendar] seems to be a global object but in fact a complete roundtrip is made to create a new calender instance. Many calls and your perfomance will be wasted. Instead you should cache the object. – Pascalius Aug 16 '12 at 09:16
  • @Pascalius Oh, of course. I thought you meant it shouldn't be used in favor of a different method. – Itai Ferber Aug 16 '12 at 13:55
  • Try setting your iDevice to japanese or thai time mode and you'll get a different year number for the same point in time. 2013 is year 25 in the Japanese calendar. – Jonny Jun 09 '13 at 05:58
  • 7
    @Jonny The results you get depend on the calendar you're using. `NSCalendar`'s `+currentCalendar` method will return the system calendar, and so if the user's phone is set to the Japanese or Thai time mode, you'll get a different year. If you want to force a specific calendar system, create a calendar and initialize it with the proper locale. For instance, to force a Gregorian calendar, you'll want to use the following: `[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]` – Itai Ferber Jun 09 '13 at 21:01
  • Uh yeah that's what I said – Jonny Jun 10 '13 at 06:47
  • @Jonny Sorry; it wasn't clear to me what you'd meant — I thought you were asking a question. My mistake. – Itai Ferber Jun 10 '13 at 12:52
  • 2
    enums NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit are DEPRECATED use NSCalendarUnitDay instead. – Kunal Balani Oct 14 '13 at 15:08
  • @KunalBalani Thanks for that — the online documentation hasn't been updated yet for `NSCalendar`, but I'll make note of that in my answer. – Itai Ferber Oct 14 '13 at 16:30
  • [NSCalendar currentCalendar] is much much faster in iOS 7 if and only if you don't call setter methods on it (i.e. the expense moves to the first setter method call, instead of the currentCalendar call). – Catfish_Man Oct 14 '13 at 17:46
  • 2
    Warning: Don't use [NSCalendar currentCalendar] unless you're trying to show a value to the user. Consider that users might follow a Buddhist calendar (the year now is 2558 or something), or any other number of odd calendars. You don't want your app to break in these cases. Use the gregorian calendar unless you have a very specific reason not to. This bug is hard to catch because you and your testers might all use gregorian as default. – n13 Aug 06 '15 at 06:28
  • how can I get the day of the year e.g 10 April is the current day is the 99th day of the current year?? – iOS Developer Apr 10 '18 at 06:52
  • @iOSDeveloper There is no date component which represents "day of the year". Instead, you can use [`-[NSCalendar ordinalityOfUnit:inUnit:forDate:]`](https://developer.apple.com/documentation/foundation/nscalendar/1408595-ordinalityofunit?language=objc) (passing in `NSCalendarUnitDay` and `NSCalendarUnitYear`, along with the date of your choosing) to achieve the same thing. – Itai Ferber Apr 10 '18 at 17:58
50

You can get separate component of a NSDate using NSDateFormatter:

NSDateFormatter *df = [[NSDateFormatter alloc] init];

[df setDateFormat:@"dd"];
myDayString = [df stringFromDate:[NSDate date]];

[df setDateFormat:@"MMM"];
myMonthString = [df stringFromDate:[NSDate date]];

[df setDateFormat:@"yy"];
myYearString = [df stringFromDate:[NSDate date]];

If you wish to get month's number instead of abbreviation, use "MM". If you wish to get integers, use [myDayString intValue];

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
skocko76
  • 533
  • 4
  • 2
  • 1
    Expanded upon his with [this gist](https://gist.github.com/2033820). Have fun--- you can drop this into your code. – DogEatDog Mar 14 '12 at 03:32
  • Solved my problem for using dates to lookup in a dictionary :) Thanks! – mlunoe Nov 12 '12 at 13:35
  • 2
    This actually doesn’t work 100% as expected all the time. For example, if the NSDate is 2013-12-31 00:00:00 +0000 then the formatted date will return 2014 for year, even if the actual number in the date is 2013. – margusholland Jan 02 '14 at 07:53
  • how can I get the day of the year e.g 10 April is the current day is the 99th day of the current year?? – – iOS Developer Apr 10 '18 at 06:54
18

Just to reword Itai's excellent (and working!) code, here's what a sample helper class would look like, to return the year value of a given NSDate variable.

As you can see, it's easy enough to modify this code to get the month or day.

+(int)getYear:(NSDate*)date
{
    NSDateComponents *components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:date];

    int year = [components year];
    int month = [components month];
    int day = [components day];

    return year;
}

(I can't believe we're having to write our own basic iOS date functions like this, in 2013...)

One other thing: don't ever use < and > to compare two NSDate values.

XCode will happily accept such code (without any errors or warnings), but its results are a lottery. You must use the "compare" function to compare NSDates:

if ([date1 compare:date2] == NSOrderedDescending) {
    // date1 is greater than date2        
}
Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159
  • 5
    The results of pointer comparison are far from a lottery - they have very well defined results, so long as you use them for what they are intended for - comparing pointers, not objects. – Richard J. Ross III May 21 '13 at 17:15
  • It might not be a lottery, but C#'s DateTime is far more intuitive. In iOS, the less-than operator really should compare two NSDates... would anyone really want to compare the pointers of two NSDate variables ? At the very least, XCode show show a Warning to ask if this is really what the user really means. – Mike Gledhill Oct 21 '13 at 11:24
  • 2
    That's just a sign of a lazy coder who doesn't understand the fundamentals of the language he's writing in. And, there have been situations for me where pointer comparisons are necessary for ObjC objects - especially if you're making a custom container (though things get weirder with ARC). – Richard J. Ross III Oct 21 '13 at 16:28
  • 1
    I must be a lazy coder who doesn't understand the fundamentals, but that's a great tip Mike! – Pier-Luc Gendreau Oct 22 '13 at 03:59
  • 4
    Pointer comparisons may be necessary but they are the far less common use case when comparing two dates. 99.99% of the time any programmer would be more interested in comparing the dates rather than the pointers. Fundamentally C# and the .NET framework were designed to make the most common scenarios easier, thus making programmers more productive, and in many cases objective-c feels like it makes things intentionally more arduous. If you have time to read an 800 page book on the fundamentals of every language, cheers, but the rest of us have apps to deliver on time and on budget. – Crake Nov 14 '13 at 02:40
  • how can I get the day of the year e.g 10 April is the current day is the 99th day of the current year?? – – iOS Developer Apr 10 '18 at 06:54
16

In Swift 2.0:

    let date = NSDate()
    let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)!
    let components = calendar.components([.Month, .Day], fromDate: date)

    let (month, day) = (components.month, components.day)
NatashaTheRobot
  • 6,879
  • 4
  • 32
  • 27
11

I'm writing this answer because it's the only approach that doesn't give you optionals back from NSDateComponent variables and/or force unwrapping those variables (also for Swift 3).

Swift 3

let date = Date()
let cal = Calendar.current
let year = cal.component(.year, from: date)
let month = cal.component(.month, from: date)
let day = cal.component(.day, from: date)

Swift 2

let date = NSDate()
let cal = NSCalendar.currentCalendar()
let year = cal.component(.Year, fromDate: date)
let month = cal.component(.Month, fromDate: date)
let day = cal.component(.Day, fromDate: date)

Bonus Swift 3 fun version

let date = Date()
let component = { (unit) in return Calendar.current().component(unit, from: date) }
let year = component(.year)
let month = component(.month)
let day = component(.day)
Mitch Haile
  • 11,716
  • 2
  • 28
  • 25
Kevin
  • 16,696
  • 7
  • 51
  • 68
9

New In iOS 8

ObjC

NSDate *date = [NSDate date];
NSInteger era, year, month, day;
[[NSCalendar currentCalendar] getEra:&era year:&year month:&month day:&day fromDate:date];

Swift

let date = NSDate.init()
var era = 0, year = 0, month = 0, day = 0
NSCalendar.currentCalendar().getEra(&era, year:&year, month:&month, day:&day, fromDate: date)
Yuchen
  • 30,852
  • 26
  • 164
  • 234
6

As of iOS 8.0 (and OS X 10) you can use the component method to simplify getting a single date component like so:

int year = [[NSCalendar currentCalendar] component:NSCalendarUnitYear fromDate:[NSDate date]];

Should make things simpler and hopefully this is implemented efficiently.

Christopher King
  • 1,691
  • 23
  • 28
5

If you are targeting iOS 8+ you can use the new NSCalendar convenience methods to achieve this in a more terse format.

First create an NSCalendar and use whatever NSDate is required.

NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *date = [NSDate date];

You could extract components individually via component:fromDate:

NSInteger year = [calendar component:NSCalendarUnitYear fromDate:date];
NSInteger month = [calendar component:NSCalendarUnitMonth fromDate:date];
NSInteger day = [calendar component:NSCalendarUnitDay fromDate:date];

Or, even more succinctly, use NSInteger pointers via getEra:year:month:day:fromDate:

NSInteger year, month, day;
[calendar getEra:nil year:&year month:&month day:&day fromDate:date];

For more information and examples check out NSDate Manipulation Made Easy in iOS 8. Disclaimer, I wrote the post.

Joe Masilotti
  • 16,815
  • 6
  • 77
  • 87
3
    NSDate *currDate = [NSDate date];
    NSCalendar*       calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents* components = [calendar components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:currDate];
    NSInteger         day = [components day];
    NSInteger         month = [components month];
    NSInteger         year = [components year];
    NSLog(@"%d/%d/%d", day, month, year);
Linh Nguyen
  • 2,006
  • 1
  • 20
  • 16
2

Try the following:

    NSString *birthday = @"06/15/1977";
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"MM/dd/yyyy"];
    NSDate *date = [formatter dateFromString:birthday];
    if(date!=nil) {
        NSInteger age = [date timeIntervalSinceNow]/31556926;
        NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
        NSInteger day = [components day];
        NSInteger month = [components month];
        NSInteger year = [components year];

        NSLog(@"Day:%d Month:%d Year:%d Age:%d",day,month,year,age);
    }
    [formatter release];
loretoparisi
  • 15,724
  • 11
  • 102
  • 146
2

Try this . . .

Code snippet:

 NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
 int year = [components year];
 int month = [components month];
 int day = [components day];

It gives current year, month, date

Sangram Shivankar
  • 3,535
  • 3
  • 26
  • 38
Yogeesh H T
  • 2,777
  • 21
  • 18
2

Here's the solution in Swift:

let todayDate = NSDate()
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)!

// Use a mask to extract the required components. Extract only the required components, since it'll be expensive to compute all available values.
let components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: todayDate)

var (year, month, date) = (components.year, components.month, components.day) 
Nitin Nain
  • 5,113
  • 1
  • 37
  • 51
2

Swift

Easier way to get any elements of date as an optional String.

extension Date {

  // Year 
  var currentYear: String? {
    return getDateComponent(dateFormat: "yy")
    //return getDateComponent(dateFormat: "yyyy")
  }

  // Month 
  var currentMonth: String? {
    return getDateComponent(dateFormat: "M")
    //return getDateComponent(dateFormat: "MM")
    //return getDateComponent(dateFormat: "MMM")
    //return getDateComponent(dateFormat: "MMMM")
  }


  // Day
  var currentDay: String? {
    return getDateComponent(dateFormat: "dd")
    //return getDateComponent(dateFormat: "d")
  }


  func getDateComponent(dateFormat: String) -> String? {
    let format = DateFormatter()
    format.dateFormat = dateFormat
    return format.string(from: self)
  }


}

let today = Date()
print("Current Year - \(today.currentYear)")  // result Current Year - Optional("2017")
print("Current Month - \(today.currentMonth)")  // result Current Month - Optional("7")
print("Current Day - \(today.currentDay)")  // result Current Day - Optional("10")
Krunal
  • 77,632
  • 48
  • 245
  • 261
2

If you wish to get the individual NSDateComponents from NSDate, you would definitely need the solution suggested by Itai Ferber. But if you want to go from NSDate directly to an NSString, you can use NSDateFormatter.

Community
  • 1
  • 1
gumbo
  • 21
  • 1
1

Swift 2.x

extension NSDate {
    func currentDateInDayMonthYear() -> String {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "d LLLL yyyy"
        return dateFormatter.stringFromDate(self)
    }
}

You can use it as

NSDate().currentDateInDayMonthYear()

Output

6 March 2016
Alserda
  • 4,246
  • 1
  • 17
  • 25
0

i do in this way ....

NSDate * mydate = [NSDate date];

NSCalendar * mycalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSCalendarUnit units = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;

NSDateComponents * myComponents  = [mycalendar components:units fromDate:mydate];

NSLog(@"%d-%d-%d",myComponents.day,myComponents.month,myComponents.year);
Shaik Riyaz
  • 11,204
  • 7
  • 53
  • 70
0

To get human readable string (day, month, year), you may do:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSString *string = [dateFormatter stringFromDate:dateEndDate];
mirap
  • 1,266
  • 12
  • 23