Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
>> Hello and welcome to
Solutions to Common Date
and Time Challenges,
I'm Chris Kane.
Now, two years ago at WWDC
2011 I gave a very similar talk
called Performing Calendrical
Calculations and I--
you can go on the develop site
at apple.com and find the video
for that and watch that too.
But I promise for this talk
I have some all new content.
So, this talk is not really--
not just a duplication
of that talk 2 years ago.
What I'm going to be
doing today is I'm going
to give you a brief introduction
to the calendar APIs,
these are the APIs that one
uses to do calendar operations,
calendrical calculations
if you will
and I'll be explaining that.
Then the bulk of
the talk is going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to be spent covering
several common tasks
that we see people doing over
and over again, and I'm going
to talk about how I
would go about, you know,
doing those kinds of operations.
Now, my examples are going to
use some new methods that are
in Mavericks OS X, Mavericks,
but not yet in iOS watch
for a future update in iOS
for those APIs to appear.
They're not here in your
seed for example, iOS seed.
Finally, I'm going to wrap
up with just a few words
about testing calendar
calculations.
And then give you some more
information about where to look
for additional resources
and stuff like that.
So, let's dive in.
So, when I talk about the
calendar APIs, I'm referring
to just these 4 classes here,
NSDate, NSDateComponets,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
NSCalendar and NSTimeZone.
Now, if you wanted to present
information to the user
on the screen like
display a date,
you would use a class
called NSDate Formatter.
I'm not going to be talking
about NSDate formatter
in this particular talk
but you can go look about--
look at the documentation
for NSDate formatter
online or an Xcode.
So, what is an NSDate?
Well, NSDate is a very
simple value object
that has just one property
which is a floating point number
of seconds relative
to our reference date.
So, basically it
just got one property
which is a double floating point
number, happens to be a double
in this case and
that's stores the number
of seconds either before or
after our reference date.
So, at this point I'm going
to introduce this graphic
which I'm going
to use throughout the
talk, this is our timeline.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, I'm going to add a
boss here to the timeline,
this little knob in the middle
and that's going to refer
in this particular case
to our reference date.
So, we're a little bit after
our reference date right now
that the actual value
of the reference date
doesn't really matter,
it's in 2001 for-- in the
Gregorian calendar as it happens
so it's a little
ways in the past
but so the numbers right now
that an NSDate their
storing is a bit--
the numbers are positive.
We're about 12 years after 2001
and so the numbers
are slightly positive
but because this is a
floating point number.
These numbers cans be very large
and, you know, go often to the,
you know, far future and refer
to dates into the far past.
They're about, what, 31 million
seconds or so in every year
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so of course after 12 years
there must be over, what,
360 million seconds that
are being as value seconds,
that are being stored in
every NSDate for dates
that around the current time.
So, these numbers are very large
of course and we don't work
with them numbers like
360 million seconds,
that's 2001 as humans and
I'll be getting to that
in a second what we do instead.
Now, in NSDate, I want
to emphasize represents
both a time and a date.
It has the word date
in there in the name
but since it is a
number of seconds
since the reference date, it
is both a date and a time.
It refers to a specific
moment in time really.
A typical thing you
might do with NSDate is
to get the current date, a date
object that represents now,
this very moment when I
making the method call.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And I'll be using this
a bunch in the talk
so that's why I'm putting
it up in the slide now.
To get the current time or
a date with the current date
in it you simply call
the class method Date.
It's very simple,
straight forward.
So, because these
numbers are very large,
of course like numbers
of seconds,
they are really cumbersome
for human to deal with.
And so, we as humans over time
have developed these notions
of what we call in this case
dateComponents that is years
and months and days and hours
and minutes and seconds.
We break time down into either,
you know, something that relates
to an astronomical cycle, or
something artificial like hours
and minutes and seconds which
are easier for us to deal with.
Now, an NSDateComponents is a
slide says here a simple model
object with stores these
kinds of components.
So, if I have NSDateComponents
object I can set it
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as month property
to 6 for example
or it's day property to 14.
Now, the other properties like--
well there's many other
properties, other properties
like hours and minutes
and seconds, or year
or what the weekday is
or those kinds of things.
I've not specified in this
date components object
of what happens with those.
Well, the default value for
every component is "unspecified"
with NSDateComponents.
So, unless a value has been set
it has this value, special value
which is basically
blank has not been set.
And there's a constant for that
NSDateComponentsUnspecified.
NSCalendars where most
of the calendar calculations
APIs occur.
NSCalendar is the object
that represents various kinds
of world calendars and knows
how to do calendar math on them.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It knows how to take an NSDate
for example and break it
down into the components like
what is the year, what month
and day within a given calendar.
And the-- you know, various
kinds of calendars include
for example the Gregorian
calendar.
That's the calendar I'm mainly
going to be using in this talk
for some of my illustrations.
Here we see a calendar
showing January 2012.
But-- and the Gregorian
calendar is of course the--
a name for the what--
some people also call
the Western Calendar.
The calendar which is very
commonly used in commerce
for example around the world
even where a local calendar
like the Hebrew Calendar
or the Islamic Calendar,
or the Thai Buddhist
Calendar is being used instead
by the local people.
So, as I say, NSCalendar knows
how to convert between NSDates,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
these very large numbers
which are not specific
to any particular calendar.
And the components that
is the human components
that humans are using with
the specific calendar systems.
NSCalendar also contains various
calendar calculation APIs.
And we'll be looking
at some of those today.
And it has several
properties as well
to control the calculation
parameters.
For example, what is the
time zone that I want
to do the calculations in.
To-- a common calendar that
when why use is the what we call
the autoupdatingCalendar.
And here I've shown a
very simple example of how
to create one of those.
The user on the system
hasn't preferred calendar.
There's a setting for that
or preference for that.
If the user changes
their preferred calendar,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you may want your
application to update
to whatever the new
users preference is.
And so, this is-- what this does
is it returns a calendar object
which watches for changes
to the user's calendar
preference and updates itself.
So, you can use this and sort
of fire and forget if you will,
and not have to worry about
the user's calendar changing.
This thing will change itself
to match whatever the user's
setting is at any given point.
So, what are these calendar
calculations that you can do?
Well, there's many
different kinds of things
that why I'm want
to do but I'm going
to give a couple
of examples here.
So, here we are the
timeline again,
I've marked June
14th and August 26th.
And one might ask for example,
how many weeks are there
between these two dates?
Well, I know between-- strictly
between these two dates
there are 72 days.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And since there's seven days
in a week, that's 10 weeks,
they're up to 10 weeks
plus two extra days
in between these two dates.
Another kind of calculation
one might do is
to ask questions about weeks.
Now, here I'm going
to show a graphic
where I've highlighted
today, June 14th.
But one thing you might notice
is for example if you're used
to United States Convention
of starting the calendars
with Sunday on the far left,
you might noticed
this is little odd.
I've chosen to start
this calendar
with Monday is the first day.
And so, I've just doing
this to highlight the fact
that different kinds of locales
might choose different first
days in the week or different
days of the week to be the--
their first day of the week.
This is a convention that you
might see for example more often
in Europe than here
in the United States.
So, one question I
might to ask is given
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that today is the 14th of June.
What is the week that
contains this date?
Well, here I've highlighted it.
June 10th was the
first day of the week
in this particular
locale of this calendar.
And the week extends
through the 16th.
NSTimeZone then I
mentioned time zones already.
NSTimeZone is our object
that represents these
time zone regions.
A time zone is the
geo-political region
like the one I've
highlighted here with Geneva,
Switzerland having
the blue dot over it.
It's a geo-political
region of the world
that has a particular
offset from Universal Time
and a particular set
of rules that determine
when that offset changes.
Generally, we call that
kind of thing for example
"daylight saving time"
or "summer time."
And you see it's a very
irregular region but, you know,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I call it geo-political.
Geo meaning, you know,
related to the earth
and it is very much vertical
band of the earth which has more
or less the same
longitudes in it.
But also that word political
is important in geo-political
because each country within
a given longitude band could
of course decide on
its own that it has one
or another different set of
rules from other countries
within the same kind of band.
And it's NSTimeZone
that has to keep track
of all these different countries
rules about, for example,
when they go into daylight
saving time or summer time.
And when they come back
out-- back to standard time.
So, as I say NSTimeZone
is the object that knows
about the local offset
from Universal Time
and when that changes.
Now, very similar to NSCalendar,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you can create an
NSTimeZone object
which we call the
local time zone.
And this object will represent
whatever the current time zone
is that the user has set
on their computer system.
Or if the user has, you know,
requested that the time zone
changes the user moves around,
this object will
update itself based
on the user's location changes.
So, let's dive in now to
this Common Operations
that I want to talk about.
And I'm going to begin by
talking about midnight.
It's very common for
people to want to know
about midnight for
various reasons.
But midnight can be
a little problematic.
And that's why I have
midnight in quotes,
and you'll see why I
have midnight in quotes
in a few bullets here.
Well, first, why do
people want midnight?
Well, one common reason is
that people are using it
as a default "don't care" time.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It is-- they may want
to use an NSDate object
to represent a date.
For example, suppose they
want to use an NSDate object
to represents somebody's
birthday.
But they don't care about
the time part that of NSDate.
You know, generally,
we don't worry
about what time people are born
within that day unless you're
say calculating astrological
charts or something like that.
So, they say to themselves
the programmers says, "Well,
I want to use an NSDate
to represent this person's
birthday, but I don't care
about what time of day
it is, so I'm going to--
I went up the kind of tidy
about things so I'm going
to set that to midnight."
Well, what I would
suggest is when you need
to do this, use noon instead.
Midnight can be problematic as
I'll described but in a moment.
So, if you really don't care
about the time use noon instead.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The other main reason
that people want to it
over midnight is they want
to know when the day changes.
So, for example, they may want
to update the user interface
of the application when the day
changes from one day to the next
which is what midnight is,
the moment at which
the next day starts.
But midnight can be troublesome.
For example, in Brazil, the
Brazilian time zone changes
by jumping forward from
midnight to 1 a.m. when they go
into their summer time.
And then the reverse happens,
you know, in their fall.
So, there may not
exist a midnight.
When 11:59 and 59
seconds comes along
and in Brazil, midnight
is skipped.
And the first moment of that
day is 1 a.m. not midnight.
So, and then, of course,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if the reverse happens
there are two midnights then
when they go back in
the standard time.
So, if you're using
midnight to mean a special--
for some special reason, you
can get in trouble of course
because there may not exist one.
Instead I would you
encourage to think in terms
of the "start of a day."
When does the day start?
What is the range
of time of a day?
And so, how do you do--
go about calculating that?
Well, this is very
common operation we found.
So, we've now added an
operation to do that.
So, to calculate today's start
what I would now do nowadays
with this new API we've added is
to call the method
startOfDayForDate.
And here I'm passing
an NSDate date which is
of course the now date, the date
representing the current moment.
For-- and that would give me
of course today's start date.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
For tomorrow's start date,
we need to do some more work.
Now, here in this
example I'm going
to be very robust
about my calculation.
So, first I'm going
to create the date
which is the startOfToday
and I do that again
with the same line of code.
Then I calculate a date
which is sometime in tomorrow
by using this method
dateByAddingUnit and passing
in the constant
NSCalendarUnitDay.
So, I'm going to add
one day to my start date
which was the previous line
here, the start of today.
And I'm not going to pass in
any options this particular case
and I have a date which
is sometime in tomorrow.
But, of course, if today's
start moment, if the--
first moment of the day was 1
a.m., well, that's not going
to be the same moment
that tomorrow
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because of the daylight
saving time transition.
I mean that's not going to
the same start of the moment,
start of the day tomorrow.
So, I have to call
startOfDateForDay--
startOfDayForDate,
excuse me, again once more
to compute the real
start moment of tomorrow.
So, anyway, let's move on.
So, why does one want
to compute midnight?
Well, the second example
I gave was you want
to do some reaction to midnight.
You want to run some code
when midnight occurs.
So, we have a new notification
for that special case
that is so very common.
And so, now you can register
for the
NSCalendarDayChangeNotification.
And you'll-- your code will get
called when the day changes.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
When midnight or, you
know, the first moment
of the next day occurs.
Why would one want to do that?
Well, maybe, you've
been awake at midnight
and had a mail application
open, and you've seen suddenly
at midnight it changes all of
the today strings in the UI
to yesterday for all
those mail messages.
Well, first thing I do to go
about doing this is
I use the local--
our local notification center
so I call the defaultCenter
method to get that.
I use the usual method
that addObserverForName
and I pass a name of
this new string constant
which represents
this notification.
There's no object or queue
that I'm going to specify
in this particular example.
And then I specify the block
of code that I want to execute
when this notification occurs.
So, it's fairly straightly
forward use
of NSNotificationCenter
in this case.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, midnight of course
is a specific example
of wanting a specific time
but sometimes people want
different specific times
but within the same day.
So, how does one go
about setting a date
to a specific time
within that date?
Well, let's suppose we want
to calculate 11:30 today,
11:30 being the time at
which this talk started.
Well, let's suppose I
have a date which is
at point A there on the graphic.
So, that would be a date
which before at a time
within this date
which is before 11:30.
Obviously, I need to compute
a date which is in the future
at which pushes the
date forward in time.
But if the data object that I
start with is at say point B,
I need to pull the
date back to 11:30.
So, what do I do?
Well, right now,
we're after 11:30
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so we're more like
at point B here.
So, to compute 11:30 of
today I'm going to start
with the current moment and
use this new method called
dateBySettingHour and set that
to 11, minute set it to 30,
and second set it
to zero toDate.
And again, in this case
there are no options
that I'm going to specify.
And what I get is the
date object that I started
with which was the current
moment has moved either forwards
or backwards in order to
set it's time to 11:30.
Another kind of question
people like to ask is,
is this date in today?
Well, it's a good
questions and very common.
So, we added a new method
for that isDateInToday,
and this returns a Boolean.
Now, of course, we didn't
just add one method here,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it's also interesting to know
about yesterday and tomorrow.
So, we added two
methods for that as well.
Now, an interesting thing
about this particular API is
that the answer is going
to change over time,
time marches forward,
and so, a date object
which represents a
time in today, well,
tomorrow it won't be a time
which is in today of course.
And so, this is an answer which
is in some sense very transient
so that's something to keep
in mind and think about.
Now, if you want
to compare dates,
we have some new API
for that as well.
NSDate itself has
a compare method.
But since NSDate is simply,
basically an object wrapper
around a floating point number.
The compare method of NSDate
is very literal when you talk
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
about wanting to know is
equal kind of comparison.
It's very literal
[inaudible] comparison.
But often people want to know
more like, you know, is this--
are these two dates, you
know, within the same day
or within the same hour?
Well, asking a question
like I just asked
on the previous slides "Is this
date today" is really just a
special case of asking
if two dates,
a date representing the
current moment and, you know,
some other given day
that you want to compare
with are on the same day.
You know, is this
date in the same day
as a date representing
the current moment?
Well, we have a new
method for this.
This is the compared
date toDate method.
You pass in the toDates
of interest.
And then as the last
parameter toUnitGranularity,
you pass in one of the
NSCalendarConstants for the unit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that you're interested in.
So, in this case I'm
going to ask the question,
are these two dates
in the same day?
But I could ask are these two
dates in the same month or year
or minute or what have you.
Now, and then, of course, if
the result is NSOrder the same
that really means that
they are in the same day.
Now, it's important to note that
this is not the same as asking
if toDates are within some
amount of one another.
So, toDates for example
can be on the same day
but they can be 23 hours and
59 minutes, sorry, apart.
But at the same time, toDates
can be just one second apart
but be on two different
days within,
you know, some given calendar.
And so, this is not the same--
I want to point out as asking
if toDates are close to one
another, but rather if they're
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on the same-- within
the same calendar unit
within the given calendar
that you're using.
Another thing people tend to do
with NSDate objects is they want
to treat them as
timeless entities,
so I call this "Timeless Dates."
Just like in my birthday
example earlier, you don't care
about the time component, you
just want to use an NSDate
to store a date, a year and a
month and a day essentially.
But NSDates are here to store
the number of seconds relative
to our reference date.
So, they don't simply store
values like Era, Year,
Month and Day which you might
want to simply store yourself.
In those cases I
would suggest using an
NSDateComponents object.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
An NSDateComponents object as I
said is very simple model object
that has separate properties for
each of the calendar components.
And so, it's very
straightforward to use one
in this way, just to store
say something like a birthday
where you just a have a
year and a month and a day.
Or, of course, another
approach would be
to create your own
simple model object
with just three integers say
or four integers if you want
to also start the Era.
And, you know, if you use
the app property syntax
in Objective-C the compiler
will create the setter
and the getter methods for you.
Now, you need-- if you do
this you need to remember
to include a property in
this object for the calendar
that these components
are relative too.
You know, a year number, a
month number, a day number,
these are just integers.
They have no meaning outside
of the context of a calendar.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You know, what calendar
is that year in?
Well, for example, I can be
talking about the year 1434.
And if you assume
that I'm talking
about the Gregorian calendar,
you say, "Oh, he was talking
about a time 600
years in the past."
But instead I'm talking
about the Islamic calendar
and that's the current year.
So, you have to have some notion
of what calendar any given set
of components are related to.
So, one approach is to include
a property for the calendar,
you use to break a date
down into those components.
Or you can define the calendar
for that class of model object
to always be a fixed value.
You can say, "Well, these
are always Gregorian dates
or these are always dates in
the Japanese Imperial calendar."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But when you do that, remember;
do not use the user's calendar
as the fixed calendar.
The user's calendar
is not a fixed value.
The user can change
that setting.
And so if you say, Well, if
you use the user's calendar
and breakdown a date
and given a set
of components are
store some information
that the user input
like the year 1434.
And then later the user
changes their calendar.
Well, now you've lost some
information because in
that year 1434 is not
necessarily the same year,
you know, it's the same
real value as would exist
in the user's new
preferred calendar.
So, do not use the
user's calendar
as a fixed calendar value.
Now, the same discussion here
applies to dateless times.
Sometimes people use
NSDate objects or try
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to use NSDate objects
to represent times
like 11:30 in the morning.
And they don't want to-- they
don't need or want to deal
with any of the higher
components
like year, month and day.
They want to use NSDate
for whatever reason
but they don't need those
higher level components.
Well, the same discussion
applies.
Use an NSDateComponents object
instead would be my suggestion
or create your own
simple model object.
Now, the bulk of the talk
here is as I wrap up is going
to focus on finding
the next matching date.
Many kinds of calendar
calculations we found can be
thought of in terms of or
expressed as the question,
"What is the next date
which matches a given
set of criteria?"
So, we've added a
new API for that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And this is perhaps
the most significant
of the new APIs we've added.
So, one thing-- one
question one might ask is
when is the next 10 a.m.?
You know, suppose I'm an
alarm clock application
and the user says, "Why do
I want to alarm at 10 a.m.?"
Well, the alarm clock
wants to know, well,
when is the next 10 a.m.
so they can fire the alarm.
Or the application might
want to know, well,
"When is the next Wednesday?"
Maybe the user has a meeting on
Wednesdays so they need to know
when is the next
Wednesday or you could ask
when is the next Wednesday
at 10 a.m. and so on.
I mean you can-- it fully
specifies a set of components
and I'll get into an
example of this in a minute.
And you ask the question, "Well,
give me the next occurrence
of this particular time
that I've specified."
So, here's one of the methods
that we've added to do this.
This is one of the simpler ones.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The nextDateAfterDate
method, matchingComponents,
options method I should say
takes a date object at which
to begin searching and
always finds an answer
which is strictly
after that date.
For the second parameter
you pass in the components
which match are to be matched.
Now, these are the
literal values.
This isn't a method
for doing sort
of arbitrary Boolean
calculations of, you know,
give me the next date where
the hour is less than 11
and the weekday is greater than
Thursday and it's a full moon.
This is-is not an API for that.
You pass in the literal
set of components
that the date should match.
But of course it doesn't have to
be complete set of components.
You don't have to specify
every component like year,
month and day and so on.
You can just say, "I want,
you know, the next 11:30 a.m."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for example.
And then you pass in as the
third argument various options.
So, let's look at this example.
I want to find the next
Wednesday at 10 a.m. So,
I create a blank or empty
NSDateComponents object
and I set two properties on it.
I set the weekday, four happens
to be the constant for Wednesday
and I set the hour to
10 for 10 a.m. Now,
of course in your
real application
and your real program,
you're not going
to have numbers right
there like 4 and 10.
You're going to have numbers
or values which you've gotten
from the user or you've
gotten from a database,
or you've gotten over
the network from--
some JSON you've parsed or
whatever it happens to be.
But for the purposes of
this particular example
to keep things a little
simpler I'm going to ignore
where these numbers came from
and just literally expose
them here on my slides.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, then I call the
method nextDateAfterDate.
I pass in NSDate date which
is the current moment.
I want to start searching now.
I passed in the dateComponents
that I just initialized.
And I passed in some options and
I'm going to ignore the question
of options for several
slides here
but I promise I will
get back to those.
Now, the question
arises what happens
to these other components
that I didn't specify?
What is the algorithm do?
You know, I didn't say
what min that I want
and I didn't say
what month I wanted.
Well, those of course as I
said towards the beginning
in an NSDateComponents
simply have a value
of NSDateComponents
and specified.
Well, that doesn't tell
us what the algorithm does
with those values, it tells
us what the values are.
For the lower components that
is the smaller components
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if you will.
The smaller cycles
that are unspecified,
what the algorithm does is
basically assume the first
or the lowest value of
that cycle can take on.
In the case of minute or second
the lowest value is zero.
So, and this of course
is easy to understand.
If I'm searching forward in time
for the next 10 a.m. the first
moment within any 10 a.m.
that I'm going to find is the
moment which has minute zero
and second zero of course.
So, what happens for
the larger components?
Let's suppose we look at
the weekOfYear component.
Well, what we do in this
algorithm is we match the same
value that the date that you
passed in, that after date,
the data which to
begin searching.
We take its value of that
component and we look for either
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that same value or the next
occurrence of that value.
Now, that's a little
complicated.
So, let me explain
with the graphic.
So, here's our June 14th again.
And suppose we're searching as--
in this example for the next
Wednesday at 10 a.m. Well,
the largest component
I specified
in my question was the
weekday, Wednesday.
So, we're going to search
within the same week
as today is 'cause I passed
in today the current moment
as my start date
to begin searching.
And we're going to search
in this week then
and the next week.
So, this is the range of
time we're going to search.
So, I ask for weekday as the
largest component I asked for.
The larger cycle,
the next larger cycle
that is of course the week.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The weekday cycle around
repeat one after the other
within the cycle of week.
And so, we begin looking in this
week but after the date I passed
in which is the current moment
and we look in the next week.
Now, of course if the start date
that I pass in is at the end
of the year or the end of the
month or the end of the year
that next week that
we're willing to search
in for an answer might
be in the next month,
it might be in the
next year even.
So, we have to do the
same kind of treatment
for all the larger components.
Now, this is the default
behavior of the algorithm.
We only search in a sense a
little ways forward in time
and find you some
answer for that.
And this-- I'm going to give an
example in several slides here
which will make this
clear why we do this
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but this the default
behavior and there's an option
for changing this
default behavior.
Let's go back to an example
I had early in the talk
which was Finding
Tomorrow's "Midnight."
Now, that took several lines
of code in the previous example
but I can rephrase that
question as one of finding.
So, I'm going to
use this new API,
create an empty NSDateComponents
object
and simply specify
the hour to be zero.
So, what this is going to do is
the algorithm is going to begin
at the date that
I'm going to give it
which is the current
moment here.
And it's going to search
forward for the next case
where a date is-- the date is
or has maybe I should
say the hour 0.
So, I pass in the dateComponents
and again I pass in some options
which I'm going to skip
over for the moment
but I promise I will
get back to those.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, it's possible that
there's no possible results.
For example, let's suppose
I initialized the day field
of my dateComponents to 50.
Well, there's no calendar
at least that I'm aware
of that has a day 50
in any of its months.
And so, if I call
the API then passing
that in what I'll
get is you know,
it's just an unreasonable
request in one sense to ask
for the next day which is 50.
So, what are these options
that I've been referring to
but I'm not explaining.
Well, I talked about
the default behavior
where we only search a
little ways forward in time.
We only search the
next occurrence
of the next larger cycle
if you will for an answer.
But if you need us
to search farther,
there's this option
NSCalendarMatchStrictly.
An example would be suppose you
wanted to find the next leap day
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the Gregorian calendar.
You want to find the
next February 29th.
Well, that could be as much
as four years in the future.
So, you need us to search
more than the current year
and the next year
for this answer.
You need to-- you
might need to search
up to four years in the future.
And so, that is the case
where you would need to pass
in this option match strictly.
Another example which I'll get
to in a minute is what happens
when a time doesn't exist
because of a daylight
savings time transition.
And again, this option has a
behavior, there in that kind
of case and I'll talk
about that in a second.
You might also want to
search backwards in times.
So, instead of just
searching forwards
for the next occurrence
even though that's common
for calendar or alarm
clock kinds of applications
in that kind of thing but you
also might want to go backwards
and so there's an
option for that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Then there are three
options for what to do,
what the algorithm should do
when the time is missing--
when there is no time
that matches that.
Now, I'm going to explain--
I'm going to dive in to
this particular example
in the next several slides.
So, I'm going to skip over
these options for the moment.
If there are multiple matches
such as there are
multiple 1:00 AM's
because Daylight Saving Time
transition has caused time
to jump backward and there are
happen to be two 1:00 AM's.
Well, there are two
options for you to tell us
which of those two options
or two alternatives you want.
NSCalendarMatchFirst is the
default option so you don't need
to specify that one if you want
the first 1:00 a.m. for example.
Now, one of these four
options is required
or will raise an exception.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You either need to ask for
strict matching or you need
to pass in one of the options
that tell us what to do
when the most desirable
time is actually missing.
So, let's look an
extended example, really,
where we're looking for the next
2:30 a.m. In the United States,
the Daylight Saving
Time transition
in the spring skips
the 2:00 hour.
And so, what does
that look like?
Well, here's our timeline again.
So, midnight of the Daylight
Saving Time transition day comes
along, time passes
and we have 1:00 a.m.
And then another hour
passes and we get
to what would normally
be 2:00 a.m.
But 2:00 a.m. hour is skipped
and it's totally missing
and so 3:00 a.m. is
actually the next time.
So, one hour after 1:00
a.m. is actually 3:00 a.m.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the United States on
the date of the transition.
And then time of course
continues as normal in 4:00 a.m.
and 5:00 a.m. come and go.
Now, if I'm looking for
2:30 in the morning,
the best result here
2:30 is actually missing.
There is no 2:30
a.m. on that day.
Well, if I pass in the
NSCalendarMatchNextTime option,
what that means is, you said,
you want the next
time which exists.
And so in this case, the
next time which exist
after the most desirable
time is 3:00 a.m. If instead,
you pass in the
NSCalendarMatchNext
TimePreservingSmallerUnits,
what we'll get is 3:00
a.m. out of the algorithm.
So, what this has
said is you want us
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to give you the best
answer within the next hour,
in this particular case,
where we give you the minutes
and the seconds that you
requested even though we can't
give you the hour
that you requested.
And then of course
there is the reverse
which is NSCalendarMatchPrevious
TimePreservingSmallerUnits.
And here you get 1:30 instead of
the most desirable answer 2:30
which of course as
I say doesn't exist.
Now, if you had passed
in instead the
NSCalendarMatchStricly option,
what happens instead is there
is no 2:30 but you've asked
for a strict match and so
the result you get is going
to be in the next day.
Well, I have a friend who
works at the local airport
and he needs to be
at work at 3:30
in the morning before the first
international flights takeoff
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and before the first
passengers arrive
for those international flights.
And he uses a fairly simple
alarm clock application
where he just specifies what
time he wants his alarm.
So, he sets his alarm for 2:30
in the morning and so he is
of course, you know,
right in the crosshairs
for this particular issue
to occur every spring.
Well, 2:30 in the
morning doesn't exist
on that transition day
in the United States.
And so, what happens?
Well, if the alarm clock
application were to pass
in the NSCalendarMatchNextTime
option.
The alarm clock is going
to ring at 3:00 a.m.
and he'll have 30 minutes
to get to work instead
of the usual hour that he needs.
If instead, it passes
in the
NextTimePreservingSmallerUnits
option, it's going to ring the
alarm at 3:30 in the morning
and he'll have no
warning to get to work.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
On the other hand, if it passes
in the PreviousTimePreserving
SmallerUnits option, it's going
to ring at 1:30 and he's going
to have an hour or two groups
of 30 minutes before and after
that missing gap of time
so he'll have his usual
hour to get to work.
So, that's sound
like a good option.
If instead it uses the
strictly option, he's not going
to get any alarm
that rings at all.
It's going to ring the next day
because it will continue
its search and find a 2:30
in the morning on the next day
and not ring this day at all.
So, what my friend really
wants to specify isn't 2:30
with his alarm clock
application.
He really wants to specify
one hour before 3:30
so it'd be much nicer
if the user interface
of this alarm clock had two
controls, one which was a time
and one which was a relative
time before that time
at which you want
to begin searching.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Of course even nicer might be
for the alarm clock application
to warn him that his desired
time doesn't actually exist,
you know, when it's like a
day before the transition
and give him an alert
saying "Hey, you know,
there's a Daylight Savings Time
transition coming up, you know,
you got an issue here."
Maybe you'd ask him, "What
do you want me to do?"
or it just says, "I'm going to
wake you up at 1:30 instead."
Another way to look
at the question
of what is the best
option is to look
at what normally
happens around this gap.
So, in the United States,
they put the transition
in the middle of the weekend.
That is early in the morning
Sundays so it's is right
in the middle of the weekend.
And so, what happens if you
just look at, say, 6:00 a.m. is
that from Friday 6:00 a.m.
to Saturday 6:00 a.m.,
there's an ordinary
period of 24 hours,
then from Saturday 6:00
a.m. to Sunday 6:00 a.m.,
there's just 23 hours.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But then again, from
Sunday 6:00 a.m.
to Monday 6:00 a.m. there's
a period of 24 hours.
So, what happens every year is
same old cliché joke is try it
out, "Oh, you're going to have
one hour less of sleep tonight."
And that happens, you know,
Saturday night all the news
reporters talk about that
to remind people about the
Daylight Saving Time change.
One hour less sleep
but that occurs
in the middle of the weekend.
That's kind of the point why
the U.S. does that transition
in the middle of the weekend.
Well, let's go back and
shift our timeline again back
to the 2:30 example.
So, if I ask for
NSCalendarMatchNextTime
to get 3:00 a.m., what will
happen is a 24 hour period
occurs from Friday
to Saturday then a 23
and a half hour period occurs
between alarm clock firings
on Sunday morning
and Monday morning.
So, you've kind of split
the different there.
On the other hand, if
you pass in MatchNextTime
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
PreservingSmallerUnits,
you get a 24-hour period
between alarm clock
firings on Saturday morning
and Sunday morning and then
your short night in a sense
or a short day is
between Sunday and Monday.
And so, it's really Monday
morning where you end
up losing that hour of sleep.
And finally, if you pass
in the MatchPreviousTime
PreservingSmallerUnits,
you get the same kind
of pattern we saw originally
where there is a 24-hour period,
a 23-hour period and
a 24-hour period.
If you need to enumerate
matches,
that is not just find one mixed
instance of a match but you want
to find all times that
match certain criteria
and enumerate them all and
do some work for each match,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
then you should use this method.
The enumerate dates starting
after date method takes the
same kind of parameters,
the first parameter, the date
at which to begin searching.
It takes your date components
that you want to have matched
and some options, the
options I've explained
and you pass in a block.
The block takes three
parameters.
The first two parameters
of the block are the date
that the algorithm found
and a Boolean telling you
whether it found an exact match
or not.
If the algorithm had to fudge
the answer based, you know,
give you a 1:30 or 3:30
instead of the 2:30 you asked
for in our previous example.
This algorithm will tell you
with that Boolean whether
it had to that or not.
Then there's the third parameter
which is the mechanism you use
to stop the algorithm
when you want to stop it.
So, we've used this in
other enumerate methods,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it's the same kind of pattern.
We pass in a pointer
to a Boolean
and when the condition
arises where you want
to stop the enumeration, you
simply set the value pointed to,
you know, star stop
in this case to "Yes".
You don't need to set the
Boolean to "No" every time
to keep enumerating, it
will keep doing that.
You don't need to
set it to know ever.
You just need to set it to
"Yes" once when you want
to stop the enumeration.
So, there were some
operations that are very common
and we found lots of
people want to do a lot.
Let me talk a little bit here,
give me few minutes of talk
about testing these algorithms
and testing the calculations
that you're doing
within your application.
Well, the first thing
you need to do is
of course consider
the interesting cases.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, what are the
interesting cases?
Well, an application today
might be used worldwide
and so you need to
consider different locales
and different calendars that
the local users, you know,
in a given area might be using.
So, you need to test different
locales and calendars.
Of course, these different users
around the world are existing
and living in different
time zones so it's good
to get some testing within
different time zones.
But you also need to
consider the cycle boundaries,
where are the interesting cases.
Well, these various cycles
of time, for example,
hours go from, say, 0 to 23
and then the cycle repeats the
next day, 0 to 23 and so on.
So, an example of a cycle
boundary is the end of a day
or the beginning of a day
is the same thing, midnight.
But you also have interesting
things that can happen
at the end of months or at
the end of years or beginning
of years, it's the same thing.
In some cases, some calendars,
the Era is really important
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to also test for
and keep in mind.
What kind of things
do you look for?
Well, you look for
incorrect results.
It's very obvious but very
dull answer, right and obvious.
Well, you look for
incorrect results.
Well, the real question here is,
how do you know that the answers
that you're seeing, that the
answers you're getting are the
correct answers?
Well, what can you do?
You have to find some sort
of authoritative information.
Like, how do I know that
it's, you know, a certain year
within the Hebrew Calendar, I'm
being told 5,700 and something.
Well, how do I know
that's correct?
Well, you have to find some sort
of authoritative information.
Well, there's these old
fashion things called Almanacs
which are going to help in
books and of course nowadays,
I tend to use the internet a lot
and so I do a search
on the internet.
What is the current
Islamic Calendar year?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
For example.
And I don't just look at
one website of course.
I look at many websites
and compare their answer.
And you can also ask people.
So, if you have people
that you know or can ask
about the answer, you know, you
can verify it with real people
and I'll get to that--
back to that in a second.
So, how do you test?
Well, to literally
test of course,
you have to turn off the
time syncing on the device,
on the computer or on iPhone
or iPad, what have you.
Then you, of course, you
can set the clock manually.
You can set the time
zone to different values.
You can set the calendar
in locale
to different values
manually and, you know,
try out your application.
But, perhaps a more
valuable thing to do is
to develop some variety
within your beta tester pool.
So, if, you know, assuming you
have some people that you ask
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to beta test your application,
you can try to collect a group
of people which live in
different time zones,
which live in different
locales of the world
and they will naturally
of course know the right answer
'cause at worse, they can look
at a newspaper or ask their
friend what the day is
if they don't know.
But they'll know what the right
answers are and they'll be able
to tell you what, you know,
what you're doing wrong
when something happens that's
wrong within their application.
But another thing you can
do is direct their testing.
Tell them, you know,
what-- if you say, "Well,
this guy in my beta
tester pool lives in China.
I know he lives in China.
You can ask him specifically,
"Can you test, you know,
this or that bit
of functionality in
this or that way?"
So, you can direct
their testing rather
than just giving them
the app and say, "Oh,
here's a new version,
go try it out."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, I've talked about various
kinds of common operations
and we've added some API for
and there are many new methods
that we added that I
didn't talk about today.
You have to go and look at
the NSCalendar documentation,
the NSCalendar.h
Header to see those.
If you had some feedback
on this talk, you can talk
to our Evangelist Paul Marcos.
The Date & Time Programming
Guide is very good.
I recommend reading that.
And of course, there's
the documentation
for the specific classes that
I talked about like NSCalendar.
And of course, the
Developer Forums, our resource
for asking questions
about these things.
And with that, I thank you.
[ Applause ]