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年後程度まで

動作環境

  • XCode5 Developer preview

  • iPhoneシミュレータ(iOS7)

  • MacOS 10.8.4