NSDate is the core class that represents a point in time but it’s pretty useless alone, in fact we can create instances of this object but without the ability to configure its details (year, month, day, hours, minutes, seconds…). Its main constructors ([[NSDate alloc] init] and [NSDate date]) return the current date and the only way to initialize a different one (a future or a paste date) with only NSDate is by using previously defined dates passing them to initWithTimeInterval:sinceDate: or dateWithTimeIntervalSinceReferenceDate: otherwise choose among one of the string based constructors that accept string representation of dates (like initWithString: or dateWithString:). NSDate doesn’t provide much more than constructors, the only concrete methods whose interact with date objects are: isEqualToDate:, earlierDate:, laterDate: and compare:. These are used to compare two dates and know which is the earlier and if their are or not the same date. Date computation such finding out years elapsed between two dates, increase a date by an arbitrary amount of days or subtract hours from a date are not possible without using other date-related classes.
NSCalendar represents a system of reckoning time over extended periods and it’s related to a concrete calendar, usually the Gregorian Calendar which is an international standard for civil use. Anyway is possible to initialize an NSCalendar choosing among hebrew, islamic, chinese an others system supported calendars. A nice (not programming related) reading about calendars and their history can be found here: http://astro.nmsu.edu/~lhuber/leaphist.html.
NSCalendar exposes methods for date computations and to extract date parts (years, months, days…) from a date.
In a calendar, day, week, weekday, month, and year numbers are generally 1-based.
A time zone represents a geopolitical region on Earth that has its own standard time. By convention each time zone computes its local time as an offset from the Greenwich Mean Time (GMT) (http://en.wikipedia.org/wiki/Greenwich_Mean_Time).
NSTimeZone is a class that handles time zones. A specific time zone can be assigned to an NSCalendar to influence its behavior.
This is a simple abstract class (no more than a structure) that incapsulate date’s parts (year, month, day, hour…). It’s used in conjunction with NSCalendar to initialize a specific user defined NSDate.
This is as special formatter class that handle conversions from date to a string and vice versa by using the conventional unicode date patterns (http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns). NSDateFormatter formats dates according to the defined locale, calendar and time zone, if you don’t specify any of these references it will uses the current system settings.
Common dates tasks
Print a formatted date:
To print a formatted date we have to use NSDateFormatter and specify a formatting mask manually (by using setDateFormat) or rely on the “automatic” masks by choosing among NSDateFormatterStyle styles.
// initialize a date object NSDate *currentDate = [[NSDate alloc] init]; // initialize a formatter object NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; // set formatter's format [formatter setDateFormat:@"eeee, dd MMMM yyyy - HH:mm"]; // print formatted date NSLog(@"formatted date: %@", [formatter stringFromDate:currentDate]);
The code above will print “sabato, 13 novembre 2010 – 12:10” on my mac due to my system settings, whose I can check as following:
// get my current time zone NSLog(@"my time zone: %@ - abbreviation: %@", [[NSTimeZone systemTimeZone] name], [[NSTimeZone systemTimeZone] abbreviation]); // get my current calendar NSLog(@"my calendar: %@", [[NSCalendar currentCalendar] calendarIdentifier]); // get my current locale NSLog(@"my locale: %@", [[NSLocale currentLocale] localeIdentifier]);
The NSLogs will print:
- “my time zone: Europe/Rome – abbreviation: GMT+01:00”
- “my calendar: gregorian”
- “my locale: it_IT”
Ok, now let’s see how we can influence the formatter by changing locale and time zone:
// create an american locale NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; // assign custom locale to the formatter [formatter setLocale:usLocale]; // print formatted date NSLog(@"formatted date: %@", [formatter stringFromDate:currentDate]);
Now it will print: “Saturday, 13 November 2010 – 12:10” despite my system locale is “it_IT”.
To influence date’s hours, I can set a different time zone rather than that one being used by my system:
// set a GMT time zone [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; // print formatted date NSLog(@"formatted date: %@", [formatter stringFromDate:currentDate]);
Now it will print: “Saturday, 13 November 2010 – 11:10“, in fact my system’s time zone is a “GMT+01:00” and if you subtract that +1 the result is 11 instead of 12!
Create an user defined date:
To create a custom date object (a date with an arbitrary year, month, day and so on), we have first to initialize an instance of NSDateComponents with the desired date’s parts and then use a calendar to retrieve the relative NSDate:
// create a custom dateComponents object NSDateComponents *customComponents = [[NSDateComponents alloc] init]; [customComponents setYear:2015]; [customComponents setMonth:7]; [customComponents setDay:23]; // create a date object using custom dateComponents against current calendar NSDate *customDate = [[NSCalendar currentCalendar] dateFromComponents:customComponents];
Get date’s parts from a date
To extract date parts from an NSDate we have to use NSCalendar‘s method components:fromDate:. Components are specified by a flag parameter which can be composed using the bitwise “or” operator (the pipe symbol) and merging NSCalendarUnit constants. To get year, month and day from a date we can do the following:
// get year, month and day parts NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];
then we can use retrieved date parts in this way:
NSLog(@"year: %i - month: %i - day: %i", [components year], [components month], [components day]);
It’s important to specify all the date parts we want to use, because if we forget it and we try to access an undefined part, we won’t get an error but instead 2147483647 as the return value (which is the maximum value for a signed 32-bit integer). For example if we write:
NSLog(@"hour: %i", [components hour]);
we will get 2147483647 because we didn’t specify the NSHourCalendarUnit
Add or subtract date parts from/to a date:
To add or subtract date parts from/to a date, we have to create a dateComponents object configuring it with the desired gap to fill and then use calendar’s method dateByAddingComponents:toDate:options:. For example if we want to add 2 years and half to a date we can do this:
// create a dateComponents with 2 years and 6 months NSDateComponents *newComponents = [[NSDateComponents alloc] init]; [newComponents setYear:2]; [newComponents setMonth:6]; // get a new date by adding components NSDate *newDate = [[NSCalendar currentCalendar] dateByAddingComponents:newComponents toDate:date options:0];
to subtract we have to configure NSDateComponents with negative values (ie: [newComponents setYear:-2]…)
Get difference between two dates:
To get the difference between 2 dates we have to rely on NSCalendar once again and use its components:fromDate:toDate method, by specifying the date parts we are interested in and 2 different dates. Values returned from components can be negative if toDate is earlier than fromDate. The following example retrieve days difference between 2 dates:
NSDateComponents *componentsDiff = [calendar components:NSDayCalendarUnit fromDate:dateA toDate:dateB options:0];
Dates are a fascinating argument when it comes to programming in Objective C, but due to the different aspects involving date computation is really important to understand concepts beyond the mere classes implementations, especially if we want to make internationalized applications. My post would be a synthesis to help you get started faster with dates manipulation, I suggest however to read all the official documentation provided by Apple.