Calendars encapsulate information about systems of reckoning time in which the beginning, length, and divisions of a year are defined. They provide information about the calendar and support for calendrical computations such as determining the range of a given calendrical unit and adding units to a given absolute time. This article describes the basic features of the NSCalendar class, and the NSDateComponents class, instances of which describe the component elements of a date required for calendrical computations.
In Mac OS X v10.4 and later and iPhone OS 2.0 and later, the NSCalendar class provides an implementation of calendars for Cocoa. Cocoa provides data for several different calendars, including Buddhist, Gregorian, Hebrew, Islamic, and Japanese (which calendars are supported depends on the release of the operating system—you should check the NSLocale class to determine which are supported on a given release). NSCalendar is closely associated with the NSDateComponents class, instances of which describe the component elements of a date required for calendrical computations.
When you create a calendar object, you specify an identifier for the calendar you want. Calendars are specified by constants in NSLocale. You can get the calendar for the user's preferred locale most easily using the NSCalendar method currentCalendar; you can get the default calendar from any NSLocale object using the key NSLocaleCalendar. Listing 1 shows how to create a calendar object for the Japanese calendar and for the current user.
Listing 1 Creating calendar objects
NSCalendar *currentCalendar = [NSCalendar currentCalendar]; |
NSCalendar *japaneseCalendar = [[NSCalendar alloc] |
initWithCalendarIdentifier:NSJapaneseCalendar]; |
NSCalendar *usersCalendar = [[NSCalendar alloc] initWithCalendarIdentifier: |
[[NSLocale currentLocale] objectForKey:NSLocaleCalendar]]; |
Here, usersCalendar and currentCalendar are equal, although they are different objects.
You represent the component elements of a date—such as the year, day, and hour—using an instance of NSDateComponents. An instance of NSDateComponents is meaningless in itself; you need to know what calendar it is interpreted against, and you need to know whether the values are absolute values of the units or quantities of the units (see “Adding Components to a Date” for an example of using NSDateComponents to specify quantities of units).
In a calendar, day, week, weekday, month, and year numbers are generally 1-based, but there may be calendar-specific exceptions. Ordinal numbers, where they occur, are 1-based. Some calendars may have to map their basic unit concepts into the year/month/week/day/… nomenclature. The particular values of the unit are defined by each calendar and are not necessarily consistent with values for that unit in another calendar.
Listing 2 shows how you can create a date components object that you could use either to create the date where the year unit is 2004, the month unit is 5, and the day unit is 6 (in the Gregorian calendar this would be May 6th, 2004), or to add 2004 year units, 5 month units, and 6 day units to an existing date. The value of weekday is undefined since it is not otherwise specified.
Listing 2 Creating a date components object
NSDateComponents *components = [[NSDateComponents alloc] init]; |
[components setDay:6]; |
[components setMonth:5]; |
[components setYear:2004]; |
NSInteger weekday = [components weekday]; // Undefined (== NSUndefinedDateComponent) |
To decompose a date into constituent components, you use the NSCalendar method components:fromDate:. In addition to the date itself, you need to specify the components to be returned in the NSDateComponents object. For this, the method takes a bit mask composed of NSCalendarUnit constants. There is no need to specify any more components than those in which you are interested. Listing 3 shows how to calculate today’s day and weekday.
Listing 3 Getting a date’s components
NSDate *today = [NSDate date]; |
NSCalendar *gregorian = [[NSCalendar alloc] |
initWithCalendarIdentifier:NSGregorianCalendar]; |
NSDateComponents *weekdayComponents = |
[gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit) fromDate:today]; |
NSInteger day = [weekdayComponents day]; |
NSInteger weekday = [weekdayComponents weekday]; |
You can configure an instance of NSDateComponents to specify the components of a date and then use the NSCalendar method dateFromComponents: to create the corresponding date object. You can provide as many components as you need (or choose to). When there is incomplete information to compute an absolute time, default values such as 0 and 1 are usually chosen by a calendar, but this is a calendar-specific choice. If you provide inconsistent information, calendar-specific disambiguation is performed (which may involve ignoring one or more of the parameters).
Listing 4 shows how to create a date object to represent (in the Gregorian calendar) the first Monday in May, 2008.
Listing 4 Creating a date from components
NSDateComponents *components = [[NSDateComponents alloc] init]; |
[components setWeekday:2]; // Monday |
[components setWeekdayOrdinal:1]; // The first day in the month |
[components setMonth:5]; // May |
[components setYear:2008]; |
NSCalendar *gregorian = [[NSCalendar alloc] |
initWithCalendarIdentifier:NSGregorianCalendar]; |
NSDate *date = [gregorian dateFromComponents:components]; |
Note that although you can configure an instance of NSDateComponents with “out of bounds” values for components (for example, in Listing 4 you could set the day component to -6), you should not rely on the result of this when when creating a date from components—the behavior may change from one release of the operating system to another.
Last updated: 2009-07-21