Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2011-117
$eventId
ID of event: wwdc2011
$eventContentId
ID of session without event part: 117
$eventShortId
Shortened ID of event: wwdc11
$year
Year of session: 2011
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2011] [Session 117] Performing ...

WWDC11 • Session 117

Performing Calendar Calculations

App Frameworks • iOS, OS X • 59:10

Calendar arithmetic can be tricky. Discover some of the common pitfalls in various calendars and learn about the best practices to doing various kinds of calculations.

Speaker: Chris Kane

Unlisted on Apple Developer site

Downloads from Apple

HD Video (149 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good afternoon and welcome to this session, Performing Calendar Calculations. My name is Chris Kane. I'm a software engineer with Apple. So what are we going to talk about in this session? Well, first I'm going to begin by reviewing what calendars are and what calendrical calculations are. Now I'm going to have to assume that you more or less know what a calendar is, what a time zone is, what time is and what dates are and so on. But I'm going to give a brief review in order to introduce some of the concepts from a point of view which will be interesting later in the talk.

Then I'm going to introduce very briefly the Cocoa APIs which are related to calendars and dates and times. But the bulk of the talk is going to be spent discussing troublesome calendrical calculation issues that, well, one can run into and also examples that we've seen people run into.

So let's begin. Well, when we talk about time, of course, we have this thing which is a continuum. One second precedes the next or follows the next, I should say. And there's this continuum going backwards and forwards in time. But people need to describe times. They need to describe when things happen and how long things take.

And so what could people do? Well, one thing we could do is observe some sort of natural cycle, like the day. You know, the sun rises, the sun sets, the sun rises again. We could see that and we could call that a day and then count the days from some starting point in the past. And under such a scheme, today could be day number 410,957. Well, that kind of number is fine for a computer.

But for people, big numbers really are not our forte. That would be really cumbersome for us to use that kind of scheme. So what do we end up with? Calendar components. These are human inventions to describe event times in a more human-friendly way. Well, one thing we do, of course, is count recurrences of natural cycles, like the day and the year.

And then what we do is we group and we decompose them into smaller, more human-tractable quantities. And in this talk, I'm going to be calling these either units or components. So what are examples? Well, of course, years, months, days, hours, minutes, and seconds. The unit of the day is too granular. That is, it's too long of a period of time for us to find really useful. So we decompose that, of course, into smaller units, hours and minutes and seconds.

But the day is also, on the other hand, too fine-grained for us to work with very conveniently. So, of course, we group days into months and years. And so we end up talking about things as humans in terms like the 8th of June, 2011, rather than day number 412. Right? 410,957.

Well, of course, over the millennia, humans have come up with and invented many different calendars. There's the Gregorian calendar, the Hebrew calendar, the Indian calendar, and so on. For those of you who may not be aware, the Gregorian calendar is the name of the calendar which is largely used in Europe and North and South America and many other parts of the world. Sometimes it's called the Western calendar. This is also the calendar that I'm going to be using throughout the examples in the rest of my talk.

What makes each calendar unique is that they have their own different ways of counting and grouping and describing the various years, eras, months, and days components. So what are calendrical calculations then? Well these are arithmetic-like operations on calendar components. So for example, I could ask the question, what day is 90 days from today? And this sounds like addition. That is, I know when today is and I want to add a component that is 90 days and compute a result date out of that.

Another kind of question I might have is, how many weeks is it until my next birthday? So here I know when today is, I know when my next birthday is, and so then I'm asking a question, well, how many weeks is it between those two dates? And so that's a subtraction-like operation.

Calendrical calculations are also involved in doing conversions between the calendars. You know, just as I had mentioned, there are many different human calendars, and often it's useful to be able to convert between them. And so the calendrical calculation operations come into play there as well. Now, the most significant point of the talk, perhaps, here today, is that calendrical calculations are subtle. Let me give an example.

So suppose we have a date like January 9th and we want to add one month. Well, the result is going to be February 9th. That is, if you notice, the month has changed because I'm adding one month, but the day has not changed. The day has been preserved, and so very naturally we get February 9th when adding one month to January 9th.

But if instead I start with the 30th of January and I add one month, well there is no 30th of February. So what can we do? Well, the most natural result for most people is to return the best answer that we can in February. And so we return the closest number, the 28th of February from this kind of operation. Well, if I then add another month to the 28th of February, I get the 28th of March. That is, the 28 gets preserved.

But if I instead start with the 30th of January and I add two months in a single calendrical operation, I end up with a result of the 30th of March. So what have we seen here? When I started with the 30th of January and I added one month and then I added another month, that is, did two operations, I ended up with the 28th of March.

But if instead I add two months in one operation, I end up with the 30th of March. So the calendrical arithmetic here is not symmetric or it's not just like ordinary arithmetic. There are subtleties introduced by the nature and the definition of each calendar. So generally, as I said, smaller components, that is, components smaller than the ones I'm changing or adding, for example, in this case, are preserved. So the day was preserved whenever possible when I was adding a month.

Now, the other main point that I want to hammer home at this point is try not to do calendrical calculations yourself. Leave them to the system whenever possible. If you find yourself doing arithmetic on a clinical component, that is for example computing a week number from some date and adding say three weeks to that with you know plus three in your source code, you might be doing something wrong.

So anytime you find yourself doing that, take a step back and think about what you're doing. But not only should you rely on the system to perform the calendrical calculations yourself, how you go about doing those calculations is also important and we'll see that in examples later on in this talk.

So let me review, for those of you who may not be intimately familiar with all of them, some of the Cocoa Calendar and Time APIs. Here's the list that I'm going to go through. So what we do in Cocoa when we defined our APIs is we started by defining a calendar independent time scale. So what we did there was we picked a reference date, a moment in time, and then we said, well, we're going to count the number of seconds from that reference date.

So a date into the future relative to that reference date is going to be a positive number of seconds, and a date in the past relative to that reference date is going to be a negative number of seconds. And we call this an NSTimeInterval. Sometimes this is also referred to, particularly in the NSDate class, as a time interval since the reference date.

All this is is a floating point number of seconds after the reference date in the reference time zone, which I'm going to interchangeably call either GMT or UTC. The value of the current NS time interval, as I'm talking right now, is about positive 329 million seconds. Again, this is a kind of thing, this time scale, which is something a computer can work with easily, but not something that humans would normally directly work with because these numbers are just way too large.

Well, when is this reference date that I'm talking about? Well, as it happens, our reference date is the first of January in the year 2001 at midnight. So, this is the second day of the Gregorian calendar. But of course, this moment in time exists in all of the different calendars. And so, for example, in the Hebrew calendar, the reference date happens to be the sixth day of Tevit in the year 5761, also at midnight. Well, sometimes you want an object wrapper that is an object form of an NS time interval, and so that's what NSDate is.

All an NSDate is, is the number of seconds that you're going to do in the year. Chris Kane All right. So, this is a floating point number of seconds. The reference date happens to be the sixth day of Tevit in the year 5761, also at midnight. Well, sometimes you want an object wrapper that is an object form of an NS time interval, and so that's what NSDate is. All an NSDate is, is the number of seconds that you're going to do in the year.

is the number of seconds, uh, a holder, an object holder, for an ns time interval, which again, is the number of seconds after the reference date in the reference time zone, GMT. NSDate as an object is not a date associated with any particular calendar. It's associated with our calendar independent time scale, NSTimeInterval. NSDates are also not associated with any particular time zone. That is, the NSDate is representing a time in the reference time zone for the world, GMT.

Then we have the class NS Calendar. And of course, as you might expect, this is the class that represents calendars. and his calendars is the object which knows about the arithmetic properties of calendars. For example, how many months are there in a year? How many days are there in each month? How many hours are there in a day? And so on.

: NSCalendar is also the object that knows how to convert an NSTimeInterval, that is a value in this calendar independent time scale, to and from dates within that particular calendar. And so to convert between two calendars, for example, normally one would take a date in that original calendar, convert it to an NSTimeInterval, and then convert that NSTimeInterval into the target calendar.

The NS Calendar class also is the object that knows how to do the kind of calendrical calculations I was just talking about, like, you know, what is the day 90 days from today? Then we have the NSDate Components class. NSDate Components is simply an object, a very simple plain object, which represents a set of calendar components. For example, you could have an NSDate Components object with a value of, say, June for the month and the 8th for the day. And that might be, you know, all there is to any particular NSDate Components object.

The component values within an NSDate components object are signed integers, and this comes into play when you use one in a relative sense. Well, an NSDate components object can be used in either of two ways. When you have what I call an absolute NSDate components object, what you have is various components like June and 8 and 4.30 p.m.

In that NSDate components object, which are representing exact values. But NSDate components as a class is also used to represent relative amounts of time. For example, As I give here, if a date component's object has a value of 4 for the hour and 15 for the minute component, it could be representing either 4:15 in the morning or it could be representing 4 hours and 15 minutes worth of time in the relative sense.

Then we have Ennis time zone. Well, the Ennis time zone represents a time zone. No surprise there. Time zone is a geopolitical region which defines a set of rules for how the local time in that region of the Earth is calculated from the reference time zone, GMT or UTC.

Usually this comes in the form of some number of hours and minutes offset from GMT. That is, you take GMT and you subtract or add some number of hours and minutes in order to compute the local time in an area. NS time zone is also the object that knows when things like daylight saving time or summertime transitions occur. So some countries change what the offset is for their local time at various times of the year. And these are usually for the reasons of like, you know, extend the number of hours of daylight in the evening during the summer, for example.

But all these things are are a transition from one offset from GMT to a different offset from GMT. The interesting thing about time zones is also that governments control and they change the rules of their time zones that affect their countries from time to time. So something that you get as a result, say in one year, you might get a different result for the same input information in another year because maybe the government that affects the time zone, controls the time zone, affected by your calculation. Maybe the government has changed the rules in the meantime.

So let's go on to Calendrical Calculations. Well, there are basically four most common NS Calendar operations that are these Calendrical Calculations. The first is Components from Date. This is a method where you give it a date and you give it the components that you want to extract, say hours and minutes and seconds or days and months or whatever it is. And it will extract those for that particular calendar. So basically this is the way you convert an NSDate to a given set of components, a representation of that date in a given calendar.

The inverse operation is Date from Components. So given a set of components, assume those components refer to values in the given calendar and give me an NSDate for that. Chris Kane So this is a very simple way to convert a NSDate from a given calendar. The inverse operation is Date from Components.

So given a set of components, assume those components refer to values in the given calendar and give me an NSDate for that. The inverse operation is Date from Components. So given a set of components, assume those components refer to values in the given calendar and give me an NSDate for that.

Then we have date by adding components to date options. This is the kind of method you would use to answer the question I had near the beginning of the talk. What is the day 90 days from today? So given an original date and a set of components, which in this case would just be 90 days, what is the resulting date? Then we have finally the components from date to date options method.

So this is the calculation I would use in order to compute the question, how many weeks is it until my next birthday? So given the two date parameters here and the component or components of interest, tell me how many of those components exist between those two dates. And of course, NS Calendar also has several other kinds of operations, but I'm not going to go into that. not going to be going into those in this talk today.

Now, there are basically two general classes of trouble that people can run into when doing calendrical calculations. The first is that calendars are irregular. They're human constructs that evolved over, you know, millennia. And so, you know, in doing the calculations, you can run into issues involving the various irregularities. And I'll be getting to examples of these in a minute.

The other class of trouble is that a set of components can be ambiguous. And there are two sub-varieties of ambiguities. One is that a set of components can specify non-existent dates, and the other is that a set of components can specify potentially multiple dates. That is, it's ambiguous which of many possible dates a set of components refers to.

So let's go into looking at some examples of irregularities. Well, the leap day in the Gregorian calendar is a good example, very simple example of an irregularity. The leap day in the Gregorian calendar is the 29th of February and it's inserted about every four years. In years which aren't leap years, there is no 29th of February, but sometimes there is a 29th of February, so this is a kind of irregularity.

Time zone transitions are also a rich source of irregularities. When you have what are often called forward transitions, that is a shift in the offset from the reference time zone, GMT, that occurs, that causes time to jump forward suddenly in a particular locale, then you have a forward transition.

And that causes an hour or potentially more hours to be skipped. Usually it's just an hour. You can also have the reverse, which is a backward transition. That is, time can appear to suddenly fall backwards, and what that causes is an hour or hours to be repeated, to occur twice.

You can also have something which is much more rare but also still a little interesting, which is a dateline transition. The reason I mention this in this particular talk is that one of these is occurring at the end of this year. What's going to happen is that Samoa is moving itself from one side of the international dateline to the other side. Now all it's doing is changing its time zone rules.

But the net effect is that it's going to be moving from sort of seeing the end of the day to seeing the beginning of the day because it will be on the western side of the international dateline. The net effect of this change is that basically 24 hours are going to be skipped in Samoa and so there will be no 30th of December in Samoa. The 29th of December will end and suddenly it will be the 31st of December.

The Hebrew calendar is another good source of irregularities. Not to pick on it, of course. Some months in the Hebrew calendar have 29 days in some years, and in other years they have 30 days. And of course there are rules that determine which is which, but some of the months have different number of days depending on what year it is.

In the Hebrew calendar, the year has either 12 or 13 months. In a year with 13 months, that's a leap year. That is, they insert a leap month rather than simply a leap day. So in the Hebrew calendar, the months are numbered from 1 through 13. And in a leap year, that is a 13-month year, I think that's sometimes called a gravid year. I'm not sure. But you have months numbered 1 through 13. But in the 12-year month, you don't have a month 7. That is, that middle month in the year is missing.

And the months go from month 6 suddenly to month 8, at least from the point of view of when you're doing calendrical calculations. The Japanese imperial calendar is also an interesting case. This is a calendar which is the same as the Gregorian calendar. That it has, you know, 12 months and has January and February and so on. Of course, you know, they call it different things in Japan. But the year numbering is what's different.

When, basically the year numbering follows the reign of an emperor. When a new emperor ascends the throne, That becomes year number one, and then year two follows that and so on. And then when the next emperor ascends the throne, that resets the numbering and it becomes year number one again. In each of these periods of an emperor's reign is what's called an era in the Japanese imperial calendar.

So what happened, for example, back at, well in this case, the end of 1988, is that we had the 31st of December in the era Shoah, the 63rd year. That was followed by the first of January, the New Year's Day, in Showa 64. Of course, you know, I'm writing Showa in English so I can remember to read it properly here when I'm giving the talk, but of course it's written differently in Japanese. So what you see here is the year changed, that is, began a new year, and the number went from 63 to 64, just as in the Gregorian calendar. Well, time passed.

And we arrive at the 7th of January in Shoa 64. And something sad happened. And the next day, there was a new emperor. A new emperor had to ascend the throne, and so, The 8th of January was in the year Heisei I. That is, a new era began because there was a new emperor.

And it was, again, year one. So what this is showing here is that not only do the year numbers change in the Japanese imperial calendar at the beginning of the year, January 1st, they can also change at any time during the year if a new emperor is needed, so to speak.

And so, obviously, in this kind of case, you can't assume that the number of a year is only going to change on January 1st, at the beginning of the year. It can change at any time, and of course that's unpredictable in the future, when that's going to happen.

So, well, enough of the examples and some of the abstractions. Let's look at a more concrete case and get into some code. So what I'm going to do is throughout this talk, I'm going to have this running example of advancing by days. Chris Kane So, given a starting date, I'm going to have a loop, and while some condition remains true, I'm going to perform some operation on my date, my starting date, and then I want to advance that date to the next day and Perform the operation again and so on in a loop. And I want my date to remain at the same time, time of, whatever its original time of day is, I want it to remain at that time as I advance from day to day to day.

So what does this look like? Well, this is fairly straightforward. I, you know, start with my starting date. In this case, I'm using the date with string method to parse a date. Nothing fancy about that. And you see I'm starting with January 1st, 2011 at midnight. So the loop, you know, performs its operation while the condition is true. And at the bottom of the loop, I need to advance the date to the next day.

So how do we do this? Well, when somebody who is unfamiliar with calendrical calculations, so we say, usually goes to do something like this, what happens is that this number appears in their source code, or a number very much like it. 86,400 is the number of seconds in a day.

So what are they doing with this number? Well, they're probably calling date by adding time interval. And what that does is it takes a number of seconds and an original date, of course, the receiving expression here, date, and it returns the new date, which is 86,400 seconds in the future.

Well, what happens when they run the code? Well, the first of January at midnight plus 86,400 seconds becomes the second of January at midnight. And the second of January becomes the third of January and so on. But eventually, at least here in the U.S., in those parts of the country that recognize and implement time zone transition, we hit the forward time zone transition.

And so at the bottom of one iteration of the loop, we have the 13th of March at midnight and we add our 86,400 seconds. And we end up getting the 14th of March at 1 a.m. And then, of course, the loop continues. And we update that time by adding 86,400 seconds. And the next iteration of the loop operates on the time 15th of March at 1 a.m.

Well, what happened here? Well, on the 13th of March, there were actually only 23 hours in that day because there was a forward time zone transition and in the United States, the 2:00 a.m. hour was skipped. So by adding 86,400 seconds, of course, we added too much and we overshot and we have fallen off of midnight.

Well instead, what we should do is what we're really wanting, you know, we should say what we mean. We should really just add one day. Use the Calendrical Calculation operations. So, I get a calendar from somewhere and I need a temporary NSDate components object initialized with the value one day. And so what I do is I create a temporary one and I set its day property to one. So, this now represents one day. It's a relative kind of NSDate components object.

So I have my original date from somewhere and I have my loop and it's performing its operation. And at the bottom of the loop, I call this method date by adding components. I'm adding that date components object, which represents one day, to my date, and I'm getting the date for the next iteration of the loop. Well, what happens when we run this? Well, things begin as before.

January 1st is followed by January 2nd as far as the loop is concerned. January 2nd plus a day is followed by January 3rd and so on. And we get to that troublesome date, the 13th of March. Well, we add one day to that and because the smaller components, smaller than a day, are preserved, we end up with the result of the 14th of March at midnight, which is exactly what we're looking for here. And then of course the 14th of March at midnight plus a day, becomes the 15th of March at midnight and so on. So, well, the issue seems to be fixed. Well, let's go back to that second category of trouble, the ambiguities within a given set of components.

So a given set of components can be ambiguous, A date cannot, a date with a given specification, if you will, may not actually exist. So in the Gregorian calendar, for example, I could say, well, 37th of June. Well, June never has 37 days or more in the Gregorian calendar, so obviously this is a trivial example of a date which doesn't exist. 29th of February, as I mentioned earlier, does not exist in most years. And so these are fairly simple and straightforward examples.

The other subcategory of trouble, ambiguity here, is that multiple dates may exist. So I could have an NSDateComponents object which represents Tuesday at 4:00 in the afternoon, Well, of course, every day has a, or every Tuesday has a four in the afternoon, and there are many of those, so this is, again, ambiguous. You don't know which particular Tuesday at four in the afternoon you're talking about.

During a summertime to standard time transition, such as occurs in the fall here in the United States, the hour is repeated in the early morning. So when we fall back from 2:59 a.m. back to 2:00 a.m. in order to repeat the second hour, well, that means there's two 2:15 a.m.s on that day.

And so if you have a date components object which specifies 2:15 a.m. on that day of a, you know, reverse time zone transition, well, you know, it's ambiguous which one you're referring to. Are you referring to the first instance of 2:15 a.m. or the second instance? So let's look a little bit more at this case of non-existent dates. So the nominal or best result of calendar arithmetic may not actually exist.

In the case of daylight saving time forward transition, where time appears to jump forward suddenly, at least in the United States, 1:59:59 becomes, one second later, 3:00 a.m. suddenly. There is no 2:00 a.m. So it really doesn't make sense to talk about times like 2:20 in the morning on the day of the transition. So if we do some calendar arithmetic, as in that code I was just looking at, we have a date, which is the day before the transition at 2:20 a.m. We add one day to that.

Well, we're going to get a result. The result which is on the day of the transition at some mystery hour. There is no 2:20 for the result to be at, but we're going to get something. Now I'm not going to explain what we're going to get exactly because it's not really pertinent to my talk. The important thing is that you get some sort of undefined mystery result.

Well, let's return to the advancing by days example. And at this point you might think, uh-oh, well, what's going on? I thought we had solved that problem. Well, it turns out that in Brazil, as at least one case in point, time zone transitions occur at midnight. So 11.59 and 59 seconds at the end of one day becomes 1 a.m. the next day then, once a day. a second later. And so there is no midnight.

So for our loop, which is trying to loop and keep the time component of that NSDate object at midnight, there's going to be trouble. At some point, the loop is going to experience the case where it has a date of the day before the transition. It's going to add one day to that, and it's going to get a day of the transition at the mystery hour as a result. Well, the loop is going to continue, and here's where the trouble really comes in.

The day of the transition at the mystery hour is going to have one day added to it and that is going to result in a time which is the day of the day after the transition, sorry, but also at the mystery hour. That is the hour, the mystery hour has been preserved and now our loop again has fallen off midnight.

Well, if you notice, this is just like what happened when we added one month to the 30th of January at the beginning of the talk and then we added another month. That is the day fell off of the 30th of the month and became the 28th of the month and we ended up with March 28th.

So what can we do about this kind of case? What if no midnight exists? Well, in our previous code, as I said, we were adding one day to our current working date within the loop to get the next date. But instead, we can add an increasing number of days to the original date and not change the original date. We can just keep adding an increasing number of days in order to compute our working date for each iteration of the loop.

So what does this look like? Well, we get a calendar from somewhere, we have an original date from somewhere, we still need a temporary NSDateComponents object. I introduce a new variable, an integer, the number of days. I initialize it to zero. I begin by initializing my working date to the original date and my loop does its operation.

Then what I do is I increment the number of days and then I set this increased number of days into the date components object. On each iteration of the loop, I'm modifying the date components operation, having an increased number of days. On each iteration of the loop, I'm modifying the date components operation, having an increased number of days.

On each iteration of the loop, I'm modifying the date components operation, having an increased number of days. Well, now what happens in Brazil? Well, eventually we get to the point where we're adding some number of days, and I call that N, where now our working date in the loop is the day before the transition at midnight.

Well, then we add n plus 1 days in the next iteration, and we get a result which is the day of the transition at the mystery hour. But then in the next iteration of the loop, when we add n plus 2 days, we get, instead of the day after the transition at the mystery hour, we get the day after the transition at midnight.

That is, we have not fallen permanently, at least as far as the loop is concerned, fallen off of midnight. We still have that issue where one of the iterations of the loop has to have some sort of mystery result because there is no midnight, but the rest of the loop is not ruined. And this is just like when we added 2 months to the 30th of January and got March 30th.

Well, let me go into a brief sidebar here and give some another kind of advice, and that is to avoid stressing boundary conditions whenever possible. Well, we've seen a few kinds of boundary conditions here. Midnight in Brazil for this particular example was a boundary condition. The end of the year is a boundary condition. And of course, the year one is another kind of boundary condition.

So what was problematic in our previous code example is that the Brazilian time zone transition occurred at midnight. And so at least one iteration of the loop could not have a result where it was working with the date at midnight. Well, other countries, it's a little more convenient for us in that they generally, other countries transition at either 1 a.m.

or 2 a.m. and so they don't, you know, skip the midnight hour and so that would have been more convenient for us. But unless you're a Brazilian voter, you have no chance at all of changing what Brazil is doing to implement its time zone rules. And so you have to deal with it.

And so one better approach is to use noon instead of midnight. Most countries are relatively sensible and they do not implement their time zone changes to occur in the middle of the day, like at noon. So instead of trying to stay at midnight as we advance through our loop, if you try to stay at noon and remain fixed to noon as your, you know, don't care time, say, that would be better.

The Samoan time zone change of not skipping the 31st of December is also probably a good idea. It might seem natural that, well, they should skip the last day of the year. If they're going to skip a day, why not the last day of the year? And so December 30th would be followed by January 1st. Well, who knows what problems that might introduce.

So instead, they're going to skip the 30th of January. Now, as an aside, I would say, well, for also a tourism-related country like Samoa, it probably makes sense for them not to skip a big party day of the year like New Year's Eve. And so that's probably why they actually are not skipping the 31st of December.

Another example that we see that's problematic is using date objects to represent just a time. I occasionally have developers come to me and say, "I want to use a date object, but I just want it to represent a time. I want it to represent, say, 4:15 in the morning, and I don't care what the date is.

I just want, for some reason, to use an end of state, but I just want the time in there to be interesting." So what they do is they use the year one, the month one, and the day one, plus their desired time. So the year 1-1-1, or the date 1-1-1, is their "don't care" time.

Well, a better approach is, well, first you could use NSDate components instead of NSDate, but another better approach is to use the date of the time interval zero in whatever calendar you're working with plus the time. So rather than using 111, use the Calendrical Calculation operations to figure out what the date of the time interval zero is and then, you know, add your time to that.

So let's get back to Calendrical Calculations. So let me talk a bit about week-based calendars. A week, of course, is a cyclic period of seven days, which are called the weekdays, and one week follows the next. Well, any calendar can be interpreted in a week-based fashion. And this can be convenient when doing calculations involving weeks or weekdays.

Like if you're doing calculations involving Wednesdays, it can be convenient to think of a calendar in a week-based interpretation. Well, so why is it that it is convenient? Well, how can you specify any given day? Well, you can specify the year and then a day number within the year. And so today might be the 159th day of 2011.

More typically what people do is they specify the year and the month of the day. And so today is the eighth day of the sixth month of 2011. But you can also use a week-based interpretation. That is, you can use what is the week-based year, the week number within a year, and the weekday. So today is Wednesday in the 23rd week of 2011. This is convenient because normally then what is changing is that week number, 23, but the Wednesday component of that triplet generally is remaining constant if you're doing usually week-based calculations.

So what is a week-based calendar? How are they really defined? Well, a week-based calendar always has an integral number of weeks. Now the regular calendar, of course, does not have an integral number of weeks. So obviously the week-based calendar is not going to be quite the same somehow as the regular calendar, the ordinary calendar.

Well, two properties define a week-based calendar. An NS calendar, the class, has two properties on it that you can change to affect both of these if you want to use it in a week-based fashion. The first property is what weekday is the beginning of the week. And of course, since a week-based calendar interpretation always has an integral number of weeks, it turns out then that of course that the year always will begin on that weekday as well. So for example, we might say Monday is the first day of the week.

The second property is a little more complex. This determines when the first week of the year is. So, as we will see in a second, you can have, at the end of the year, normally you would have a week which is straddling both part in the old year and part in the new year.

And so you have to decide, well, which year is that week going to be in? And the second property of the minimum number of days that the week has to have in the new year to be considered in the new year is determined, makes that decision for you.

The ISO 8601 calendar defines one example of a week-based calendar. So this isn't just theoretical. There's an international standard which uses and defines a week-based calendar. Well, what happened, what was an example of this? It's a little easier to see in graphical form. At the end of last year, what we had was the days, something like this. And I've drawn the chart here starting on Saturday. So, suppose our first day of the week is Monday. That means December 20th began a week, and that went through December 26th. Well, there's no particular controversy here. This is a week within the year 2010.

But this week, the next week, straddles both years. And so which year is this week going to be numbered as? All the days in this week have to be either considered in 2010 or in 2011 under a week-based calendar interpretation. Well, let's suppose that our minimum number of days that the week has to have within the new year property is four.

Well, this week has two days within the new year, which is less than four, and so this week wouldn't be considered within the new year, 2011, so this week is 2010. And the result is then that January 1st and January 2nd are... numbered, or have a year number of 2010 under a week-based calendar interpretation of the calendar.

Well, then the next week begins on January, Monday, January 3rd, and of course, there's no particular controversy here. This week entirely falls within 2011, and so these days are numbered with a year number of 2011. Now, if we go back here to this middle week, suppose I said the minimum number of days in the first year of the calendar is a year.

The first week was one. So this week has two days in the new year, and so that's bigger than one. And so under that kind of rule, this week would be entirely assigned to the year 2011. And so the week-based calendar year 2011 would be beginning Monday, December 27th.

Well, where do people get into trouble when dealing with week-based calendars? Well, as I've just been talking about, the year number for days in a week-based calendar interpretation may not be the same year number as those days under the ordinary calendar interpretation. It might be different for some number of weeks, a few days on either side of the new year, those two numbers. Sorry.

So you must not mix the ordinary year number in with your week-based calculation or week-based components. And similarly, you must not mix the week-based year number in with your ordinary components. And this can also cause ambiguity. For example, there is no 2nd of January in 2011, where 2011 here is the week-based calendar year number, as in ISO 8601. For example, in the ordinary calendar, the 2nd of January 2011 is actually the 7th day of the 52nd week of 2010 in the ISO calendar.

If instead I was saying 2011 trying to mean the week-based calendar year, well, there is no such day. That particular day that I'm trying to specify is actually the first day of the first week of the year 2012. And so in 2011 in the ISO calendar, there is no second of January. So it doesn't make sense. That's why it's in quotes. There is no second of January 2011 where the 2011 is a week-based calendar year.

Well, to help with week-based calculations, we've added some new API in ISO 5, IOS 5, and Mac OS X 10.7. We've added some new calendar component constants. Well, the existing calendar component constant, NSYearCalendarUnit, always specifies now the ordinary calendar year. and we've added some new constants. The first is the week of year calendar unit.

So now you can talk explicitly in terms of what is the week number of the year. And similarly, we've added a week of month calendar unit. Generally, in many calculations I should say, not generally, but in many calculations these are actually the same. There's no particular difference, but sometimes the, which one you specify is actually crucial.

But most importantly, we added then an NSYear for Week of Year calendar unit, and this allows you to get and work with the week of year year. So there's an existing constant, the NSWeek calendar unit, and we discourage that to some extent. That is, it seems preferable to us for you to use one of the two new constants when you can because you are saying what you mean.

But of course, if you have to run on older versions of the operating system, then 10.7 or 5.0, then you can't use that. So we haven't actually deprecated this. We just encourage you to say more explicitly what you mean by using one of the new constants when you can.

So let me go on to date formatting and parsing. There are a couple issues here that I want to bring up. So an NSDate formatter is an object which knows how to convert dates to strings and strings back to dates. Usually this is used to do it in a locale sensitive fashion. That is, when you want to present a date to the user, you usually use an NSDate formatter so that you're presenting something that the user will most naturally understand. understand.

Well, the first issue that arises goes something like this. Suppose that the developer writes this particular code up here sometime in the middle of year 2010. And so what they've done is they've created a date formatter and they did a bunch of configuration on it. Then they set its date format specification pattern to this YYYMMDD, and what they want are strings like year, month number, and day number.

Well, this code appears to work when they run it. So, you know, everything seems happy and the app ships. But then time passes and somebody runs the app on January 1st of 2011 and the app is now showing, for some reason, 2010-01-01. What's going on? There's an off-by-one problem here because, well, obviously 2010 is not the correct year number, 2011. Well, what's actually going on here is that all capital YYYY is the week-based calendar year.

So the developer has asked the system to put the week-based calendar year, which can be different from the ordinary calendar year that most users might be expecting, into their strings. And so that's why they got the 2010 result. What the developer wanted to do instead, and this is, of course, admittedly very subtle, is to use lowercase. YYYY and that gives you the ordinary calendar year.

The second gotcha I want to cover today is exemplified here. So here the developer is setting the date format specification pattern to have years, months, and days, and then hours, minutes, and seconds in the resulting string when they format a date. That capital HH there in the middle means that the developer wants to get a 24-hour clock.

They want the string to have any number from 00 to 23 in the hour position when dates are formatted. Well, what happens then when the developer runs their program, and it doesn't have to be January 1st, I just picked on January 1st again, is that they run the code and for some users, they see this, which is exactly what the developer wanted. Okay.

But other users, mysteriously, see this. They see 2:00 p.m. instead of 1400 hours. Well, what's going on here? Oh, well, and another point is that if you're going in the reverse direction, that is parsing strings back into dates, one of these strings is going to work with this date formatter, and the other one is not, depending on the user.

Well, what's going on is that date formatters start with the current user locale object when they're created and initialized. And the current user locale object is an object which has various user settings in it as well and user preferences, whatever you want to call them, as well as various other bits of locale data. And some of those user preferences override even a specifically set format pattern on a date formatter object.

So even though the developer asked for 24-hour clock, because they're using a date formatter with the current user locale in it, they might get, you know, something else. And in particular, the 24-hour time setting that one can find in iOS, like as I'm showing here in the iPhone, is one of these settings that can override even what the developer is explicitly asking for.

So the result is that the developer has asked for 24-hour clock. can be either 1400 or 2:00 PM. Well, what's the fix here? Well, this is a case where the developer wants to get a specific result, and so the current user locale object should not be used. The locale object needs to be set to something else on the date formatter.

For Internet-type date strings, the locale named enusposix often works well. And so, very simple to use this, or to create one of these. You simply call init with locale identifier with enus posix, and you set that locale object into your date formatter, thus erasing its initial state of the current user locale object. And then, of course, I have to release my locale object here just to clean up.

And so what that will do is use the so-called ENUS POSIX locale in order to format dates that are given to the date formatter. Well, okay, so that's been a lot of information, but I see I'm running out of time, so I need to wrap up. So our Application Frameworks Evangelist is Bill Dudney, and here's his email address. For more documentation on the classes that you've seen me talk about here today, go refer to the Cocoa documentation, either from Mac OS X or iOS Foundation.

Here's an example of one of the links to get to that. And, of course, many people who know about dates and times and calendrical calculations, and myself included, when I have time, lurk on the Apple Developer Forums. So what are the takeaway points that I've given here? Well, use the system calculation algorithms whenever possible. Don't try to write them yourself. They're complex. Even we get them wrong once in a while, and we apologize for that in advance.

But still, care must be taken in how you use them, as I showed with that loop, which, you know, naturally one would think of advancing by days, but in the special case of Brazil and potentially other time zones, which transitioned at midnight, you know, there were still issues with that particular solution, so we had to come up with yet another solution.

Try to avoid boundary conditions, stressing boundary conditions whenever possible. But also, when you're writing your app, try to imagine what the interesting boundary cases are for testing. So, for example, if you advance the date on your device, say iOS device, to near the end of the year, you might turn up an issue like that date formatter problem where the wrong year was showing up in the UI. Well, and with that, thank you for your time.