[objective-c] NSDate get year/month/day

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?

This question is related to objective-c nsdate nsdatecomponents nscalendar

The answer is


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.


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];

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

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.


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

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.


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

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];


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        
}

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

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)

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)

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.


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) 

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


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)

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

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSString *string = [dateFormatter stringFromDate:dateEndDate];