NSDate initWithTimeInterval:sinceDate:でX年後の日付を求める際の注意点
現象
- NSDate の initWithTimeInterval:sinceDate: メソッドを使って簡易的に100年後の日付を求めようとしたら値がおかしい。
ソースコード(誤)
#define kOneDay (24*60*60)
#define kOneYear (365*kOneDay)
- (NSDate*)test
{
NSDate* date = [NSDate date];
NSDate* laterDate = [[NSDate alloc]
initWithTimeInterval:100*kOneYear
sinceDate:date];
// for debug
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents* dc = [calendar components:NSYearCalendarUnit fromDate:laterDate];
NSLog(@">>> year = %d", [dc year]);
return laterDate;
}
結果
>>> year = 1977
どうやら、桁あふれが起きている。
TimeInterval で表現できるのは60年くらいまでのようだ。
どおりで initWithTimeInterval: のサンプルコードで○年後というのは見ないわけだ…。
上記ではうるう年等の正確な計算もできないので、NSDateComponents を使うよう修正。
ソースコード(正)
- (NSDate*)test2
{
NSDate* date = [NSDate date];
NSCalendar *calendar = [[NSCalendaralloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents* offset = [calendar components:NSYearCalendarUnitfromDate: date];
[offset setYear:100];
NSDate* laterDate = [calendar dateByAddingComponents:offset toDate:self.birthday options:0];
// for debug
NSCalendar *debugCalendar = [[NSCalendaralloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents* dc = [debugCalendar components:NSYearCalendarUnitfromDate:laterDate];
NSLog(@">>> year = %d", [dc year]);
return laterDate;
}
結果
>>> year = 2113
ちゃんと出た。
結論
- 「○時間後」「○日後」は initWithTimeInterval でいいが、「○年後」はうるう年の考慮もできる NSDateComponents の dateByAddingComponents:toDate:options: を使う。
- TimeInterval で表せるのは60年後程度まで