西海岸より

つらつらざつざつと

NSDateFormatterのYYYY利用時の注意点


NSDateFomatterを利用していてハマったところ。
以下のコードでは、originalという年月日時分秒の文字列を、
フォーマッタで日付変換し、さらに同じフォーマッタで文字列に戻すというもの。
結果は元の文字列になるはず。(original = originalReversed)
(フォーマッタを使ったメソッドのテストコードの一部)

    NSString *original = @"20100101123456";
    NSLog(@"original is %@", original);  //originalを表示
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"YYYYMMddHHmmss"];
    
    // NSString -> NSDate
    NSDate *originalDate = [formatter dateFromString:original];    
    NSLog(@"originalDate is %@", originalDate);  //originalをNSDateに変換したもの
    
    // NSDate -> NSString
    NSString *originalReversed = [formatter stringFromDate:originalDate];
    NSLog(@"originalReversed is %@", originalReversed);  //originalと同じになるはず
    
    [formatter release];

なんとこれが以下のような結果に。。
リバースすると一年ずれてしまう。正確には、NSDateから文字列に変換する際にずれてしまっているような感じ。

original is 20100101123456
originalDate is 2010-01-01 03:34:56 GMT
originalReversed is 20090101123456

ちなみに、originalの日付を正月では無く、2010103とした場合には問題ない。
(20100102だと一年ずれる)

original is 20100103123456
originalDate is 2010-01-03 03:34:56 GMT
originalReversed is 20100103123456

他の月は問題ないが、20110101でも同様に1年前の2010になってしまう。
正月付近の挙動があやしすぎる
いったいなぜ。。。フォーマッタの定義を見てもよくわからず。
(既知のバグっぽいけど)

解決方法

  • YYYY を yyyyに変更

たったこれだけで解決。web上のいろんなサンプルでもYYYYでしているところがあるので、気をつけよう。

理由

ちなみにこうなる理由を調べたところ、フォーマッタはUnicode形式に従うものらしく、
Unicodeの仕様には以下のように書いてある。

Year (of "Week of Year"), used in ISO year-week calendar. May differ from calendar year.

Y(大文字)はその週の年、つまり1月1日が週の後半(厳密には木曜日以降)だったら、その週は前年の週と見なされる。
2010年1月1日は金曜日なんで2009年の週ということ。2日の土曜も同様。

初めて聞く仕様だ。
使うことはないけれど注意しないと。