FROM : Chris Kane
DATE : Fri Feb 01 19:41:35 2008
On Jan 28, 2008, at 3:49 PM, Nick Zitzmann wrote:
> What's the best way of getting the start date for a given era in an
> NSCalendar?
>
> For days, weeks, etc. this is trivial, since they have a universal
> start time. But an era can begin and end at any point in time.
> These points in time are trivial to figure out on the Gregorian
> calendar (the AD era began on January 1, 1), but the Japanese
> calendar is a totally different story (the Heisei era began on
> January 7, 1989 for example).
>
> I've tried to solve this by polling NSCalendar for era changes, by
> getting the date for the first day in the era (which may not
> actually be in the era) and stepping through days until the era of
> the day matches the desired era, but this takes way too long to
> calculate for all eras on the Japanese calendar, so I'm hoping
> there's a better way.
In 10.5 and later, there is the new -
rangeOfUnit:startDate:interval:forDate: method in NSCalendar for just
this sort of thing.
#import <Foundation/Foundation.h>
int main() {
[NSAutoreleasePool new];
id cal = [[[NSCalendar alloc]
initWithCalendarIdentifier:NSJapaneseCalendar] autorelease];
NSDate *date = nil;
NSTimeInterval ti = 0.0;
BOOL b = [cal rangeOfUnit:NSEraCalendarUnit startDate:&date
interval:&ti forDate:[NSDate date]];
NSLog(@"Start date is '%@', interval is %f, succeeded? %s", date,
ti, b ? "yes" : "no");
return 0;
}
% cc test.m -framework Foundation
% ./a.out
2008-02-01 09:48:17.115 a.out[38611:10b] Start date is '1989-01-08
00:00:00 -0800', interval is 2019686400.000000, succeeded? yes
%
Though note that the time zone there is a little funky. Could be an
issue with date display, or with the computation/database not taking
the time zone into account, or with it actually taking it into
account when I wasn't expecting it to. Of course, that raises the
interesting question of whether the era did begin on that date (in
the Gregorian calendar) at midnight GMT+0, or whether midnight in
Tokyo would be a better answer, in which case the answer is about 17
hours off.
If I set the time zone of the calendar for calculation purposes to :
[cal setTimeZone:[NSTimeZone timeZoneWithName:@"Asia/Tokyo"]];
Then the answer is:
2008-02-01 10:03:24.325 a.out[38637:10b] Start date is '1989-01-07
07:00:00 -0800', interval is 2019686400.000000, succeeded? yes
(when displayed in the Gregorian calendar in my default time zone,
GMT-0800).
How does NSCalendar do this? You'd never guess. It gets close, then
"steps through days until the era of the day matches the desired
era". The actual era database is buried in the ICU library and isn't
readily accessible.
> takes way too long [...]
One way to handle this with calendrical walking forward/backward
algorithms is binary search: with a date starting in the previous
era, pick a k > 0, and try adding 2^k seconds; if that puts you in
the next era, you've overshot, decrement k and start over; if the new
date is still in the previous era, set that to be your current date,
decrement k, and start over; until you overshoot when k = 0. As long
as k starts sufficiently large, you will never successfully need to
add each 2^k more than once, or you would have succeeded on the
previous iteration when k was one larger (2 * 2^k == 2^(k+1)), and so
you can decrement k on each iteration. Starting around k = 37 is
usually plenty, so you'd have up to ~37 probes.
Chris Kane
Cocoa Frameworks, Apple
DATE : Fri Feb 01 19:41:35 2008
On Jan 28, 2008, at 3:49 PM, Nick Zitzmann wrote:
> What's the best way of getting the start date for a given era in an
> NSCalendar?
>
> For days, weeks, etc. this is trivial, since they have a universal
> start time. But an era can begin and end at any point in time.
> These points in time are trivial to figure out on the Gregorian
> calendar (the AD era began on January 1, 1), but the Japanese
> calendar is a totally different story (the Heisei era began on
> January 7, 1989 for example).
>
> I've tried to solve this by polling NSCalendar for era changes, by
> getting the date for the first day in the era (which may not
> actually be in the era) and stepping through days until the era of
> the day matches the desired era, but this takes way too long to
> calculate for all eras on the Japanese calendar, so I'm hoping
> there's a better way.
In 10.5 and later, there is the new -
rangeOfUnit:startDate:interval:forDate: method in NSCalendar for just
this sort of thing.
#import <Foundation/Foundation.h>
int main() {
[NSAutoreleasePool new];
id cal = [[[NSCalendar alloc]
initWithCalendarIdentifier:NSJapaneseCalendar] autorelease];
NSDate *date = nil;
NSTimeInterval ti = 0.0;
BOOL b = [cal rangeOfUnit:NSEraCalendarUnit startDate:&date
interval:&ti forDate:[NSDate date]];
NSLog(@"Start date is '%@', interval is %f, succeeded? %s", date,
ti, b ? "yes" : "no");
return 0;
}
% cc test.m -framework Foundation
% ./a.out
2008-02-01 09:48:17.115 a.out[38611:10b] Start date is '1989-01-08
00:00:00 -0800', interval is 2019686400.000000, succeeded? yes
%
Though note that the time zone there is a little funky. Could be an
issue with date display, or with the computation/database not taking
the time zone into account, or with it actually taking it into
account when I wasn't expecting it to. Of course, that raises the
interesting question of whether the era did begin on that date (in
the Gregorian calendar) at midnight GMT+0, or whether midnight in
Tokyo would be a better answer, in which case the answer is about 17
hours off.
If I set the time zone of the calendar for calculation purposes to :
[cal setTimeZone:[NSTimeZone timeZoneWithName:@"Asia/Tokyo"]];
Then the answer is:
2008-02-01 10:03:24.325 a.out[38637:10b] Start date is '1989-01-07
07:00:00 -0800', interval is 2019686400.000000, succeeded? yes
(when displayed in the Gregorian calendar in my default time zone,
GMT-0800).
How does NSCalendar do this? You'd never guess. It gets close, then
"steps through days until the era of the day matches the desired
era". The actual era database is buried in the ICU library and isn't
readily accessible.
> takes way too long [...]
One way to handle this with calendrical walking forward/backward
algorithms is binary search: with a date starting in the previous
era, pick a k > 0, and try adding 2^k seconds; if that puts you in
the next era, you've overshot, decrement k and start over; if the new
date is still in the previous era, set that to be your current date,
decrement k, and start over; until you overshoot when k = 0. As long
as k starts sufficiently large, you will never successfully need to
add each 2^k more than once, or you would have succeeded on the
previous iteration when k was one larger (2 * 2^k == 2^(k+1)), and so
you can decrement k on each iteration. Starting around k = 37 is
usually plenty, so you'd have up to ~37 probes.
Chris Kane
Cocoa Frameworks, Apple
| Related mails | Author | Date |
|---|---|---|
| Nick Zitzmann | Jan 29, 00:49 | |
| ? ?? | Jan 29, 01:39 | |
| Clark Cox | Jan 29, 01:45 | |
| Nick Zitzmann | Jan 29, 02:54 | |
| Christopher Nebel | Jan 29, 05:11 | |
| Nick Zitzmann | Jan 29, 05:31 | |
| ? ?? | Jan 29, 06:01 | |
| Christopher Nebel | Jan 29, 22:17 | |
| Chris Kane | Feb 1, 19:41 |






Cocoa mail archive

