iPhoneで和暦表示設定のとき、日付や曜日がずれる
GoogleCalendarの予定をiPhoneに表示するアプリを作成中、
実機で動かすと、2012年4月1日の曜日が1日ずれていることを発見。
4000年4月1日(土) ←2012年4月1日は日曜日…
ん?4000年??
年まで変わってる!
iPhoneの設定で、カレンダーの設定を和暦にしているとこうなる。
西暦に変更すればちゃんと「2012年4月1日(日)」と表示されるのだが。
よく分からないので、Googleで調べてみると、以下のページを発見。
カレンダー設定を和暦にすると2011年が西暦3999年と解釈されてしまう - 酢ろぐ!
まさに同じ状態。文字列からNSDate型に変換するとき、逆にNSDate型から文字列に変換するときには「NSGregorianCalendar」を使う必要があるようだ。
他のブログには、NSJapaneseCalendarの記事もあった。
カレンダーにもいろいろと種類があるんだな。*1
文字列からNSDate型へ変換
文字列を切り取って、NSDateComponentsにセットし、
そこからNSDate型変数を作成する。
NSDateComponents *comps = [[NSDateComponents alloc] init]; NSDate *date; @try { [comps setYear: 2012]; // Year [comps setMonth: 4]; // Month [comps setDay: 1]; // Day [comps setHour: 15]; // Hour [comps setMinute: 0]; // Minute [comps setSecond: 0]; // Second } @catch (NSException *exception) { NSLog(@"error was cought at NSDateComponents set values"); } NSCalendar *calendar = [NSCalendar currentCalendar]; NSCalendar *gregorianCalendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar]; NSCalendar *japaneseCalendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSJapaneseCalendar]; date = [calendar dateFromComponents:comps]; // currentCalendarで実行 NSLog(@"calendar date[%@]", date); date = [gregorianCalendar dateFromComponents:comps]; // NSGregorianCalendarで実行 NSLog(@"gregorianCalendar date[%@]", date); date = [japaneseCalendar dateFromComponents:comps]; // NSJapaneseCalendarで実行 NSLog(@"japaneseCalendar date[%@]", date);
実行結果をまとめるとこんな感じ。
西暦設定 | 和暦設定 | |
currentCalendar | 2012-04-01 06:00:00 +0000 (西暦2012年) | 2012-04-01 06:00:00 +0000 (平成2012年) |
NSGregorianCalendar | 2012-04-01 06:00:00 +0000 (西暦2012年) | 0024-04-01 06:00:00 +0000 (平成24年) |
NSJapaneseCalendar | 4000-04-01 06:00:00 +0000 (西暦4000年) | 2012-04-01 06:00:00 +0000 (平成2012年) |
※()内は追記した
数字だけを見ていれば「currentCalendar」で問題なさそうだが、
システムの表示形式が変わっているため、「currentCalendar+和暦設定」では平成2012年になるのだ Σ(゚д゚lll)
ちなみによく見ると、時間が9時間ずれている。
Timezoneが「+0000」になっているため、合っているのだがわかりにくいな(´・ω・`)
NSJapaneseCalendarでは、NSDateを作るときに「2012年」を渡しているが、
これが「平成2012年」と解釈されるため、西暦にすると「西暦4000年」になっている。
つまり、NSJapaneseCalendarを使うときは、年は和暦にして作業しなければならないということ。
もろもろ考えると、日付は西暦のGMT(標準時)で作業することに統一して、
NSGregorianCalendarを使うことにしたほうがよさそう。
NSDate型から文字列を作成
今度は逆にNSDate型から日付文字列を作るときの話。
NSDateFormatter *format; NSString *str; NSDate *date = [NSDate date]; // 現在時間で作成 format = [[NSDateFormatter alloc]init]; [format setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"]]; [format setDateFormat:@"yyyy年MM月dd日(EE) HH時mm分ss秒"]; // 時間文字列のフォーマット定義 NSCalendar *calendar = [NSCalendar currentCalendar]; NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSCalendar *japaneseCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSJapaneseCalendar]; [format setCalendar:calendar]; // currentCalendarで実行 str = [format stringFromDate:date]; NSLog(@"currentCalendar date[%@]", str); [format setCalendar:gregorianCalendar]; // NSGregorianCalendarで実行 str = [format stringFromDate:date]; NSLog(@"NSGregorianCalendar date[%@]", str); [format setCalendar:japaneseCalendar]; // NSJapaneseCalendarで実行 str = [format stringFromDate:date]; NSLog(@"japaneseCalendar date[%@]", str);
実行結果はこんな感じ。
西暦設定 | 和暦設定 | |
currentCalendar | 2012年03月31日(土) 20時32分27秒 | 0024年03月31日(土) 20時33分58秒 |
NSGregorianCalendar | 2012年03月31日(土) 20時32分27秒 | 2012年03月31日(土) 20時33分58秒 |
NSJapaneseCalendar | 0024年03月31日(土) 20時32分27秒 | 0024年03月31日(土) 20時33分58秒 |
西暦表示したいときは「NSGregorianCalendar」を使い、
和暦表示したいときは「NSJapaneseCalendar」を使えばいいようだ。
「currentCalendar」はカレンダーの設定が西暦の時には「NSGregorianCalendar」
和暦の時には「NSJapaneseCalendar」を使っているような動きをしているけど、
アプリが西暦のつもりでユーザが和暦設定にすると、変な値(西暦4000年など)になるから使わない方がいい。
欧米なんかはカレンダーの設定を変えようがないだろうから、currentCalendarでも問題は起きないだろうけど、
日本や中国など、独自の暦を使っている環境だと、ユーザ設定によってこんな問題が起きるんだなと。
LocaleやTimezoneは付属的なものだからいいとしても、
日付処理はなかなかやっかいだな。
*1:詳しく知りたいときは「NSLocale.h」を参照