強火で進め

このブログではプログラム関連の記事を中心に書いてます。

NSDateFormatter - 文字列と日付の変換

文字列→日付、日付→文字列の変換を行うNSDateFormatter、理解するまではちょっと難しそうな印象があるこのクラスの使い方を解説します。

フォーマットの指定

文字列 intputDateStr を inputDateFormatter で指定したフォーマットでパースしてNSDate に格納。それを outputDateFormatter で指定したフォーマットで文字列として出力するサンプルです。

	NSDateFormatter *inputDateFormatter = [[NSDateFormatter alloc] init];
	[inputDateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss"];
	NSString *intputDateStr = @"2000/01/02 03:04:05";
	NSDate *inputDate = [inputDateFormatter dateFromString:intputDateStr];
	
	NSDateFormatter *outputDateFormatter = [[NSDateFormatter alloc] init];
	NSString *outputDateFormatterStr = @"yyyy/MM/dd HH:mm:ss";
	[outputDateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"JST"]];
	[outputDateFormatter setDateFormat:outputDateFormatterStr];
	NSString *outputDateStr = [outputDateFormatter stringFromDate:inputDate];
	NSLog(@"[in]%@ -> [out]%@(%@)", intputDateStr, outputDateStr, outputDateFormatterStr);
	[inputDateFormatter release];
	[outputDateFormatter release];

outputDateFormatterStrの文字列を変更(出力フォーマットを指定)する事により以下の様な出力が可能です。

フォーマットの指定 入力 出力 説明
yyyy/MM/dd HH:mm:ss 2000/01/02 03:04:05 2000/01/02 03:04:05 全てをゼロ埋め有りで出力。
yyyy/M/d H:m:s 2000/01/02 03:04:05 2000/1/2 3:4:5 月・日・時・分・秒のゼロ埋め無し。
yyyy/M/d H:m:s 2000/11/22 13:44:55 2000/11/22 13:44:55 もちろんゼロ埋め無しでも2桁のデータが入力された場合は2桁の出力に成ります。

タイムゾーン

フォーマットの指定 入力 出力 説明
yyyy/M/d H:m:s z 2000/01/02 03:04:05 2000/1/2 3:4:5 JST zでタイムゾーンの出力が出来ます。
z zz zzz zzzz 2000/01/02 03:04:05 JST JST JST 日本標準時 zが1〜3つのときは英語の大文字の省略されたもの(日本だと3文字ですが国により常に4文字の場合も有ります)。zが4つの場合は詳細な説明文となります。
Z ZZ ZZZ ZZZZ 2000/01/02 03:04:05 +0900 +0900 +0900 GMT+09:00 Zが1〜3つのときは世界標準時からの差(日本の場合は+9時間)。Zが4つの場合は GMT に時差を加えたものになります。

JSTなどの省略された文字列については以下を参照下さい。

標準時 - Wikipedia
http://ja.wikipedia.org/wiki/%E6%A8%99%E6%BA%96%E6%99%82

タイムゾーンJST から PST に変更すると以下の様に成ります。

フォーマットの指定 入力 出力
yyyy/M/d H:m:s z 2000/01/02 03:04:05 2000/1/1 10:4:5 GMT-08:00
z zz zzz zzzz 2000/01/02 03:04:05 GMT-08:00 GMT-08:00 GMT-08:00 アメリカ太平洋標準時
Z ZZ ZZZ ZZZZ 2000/01/02 03:04:05 -0800 -0800 -0800 GMT-08:00

Locale(地域指定)をUSにする

Localeの設定次第でも結果が異なります。
例えばLocaleをUSにしたい場合。プログラムは以下の様に記述します。

	NSDateFormatter *inputDateFormatter = [[NSDateFormatter alloc] init];
	[inputDateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss"];
	NSString *intputDateStr = @"2000/01/02 03:04:05";
	NSDate *inputDate = [inputDateFormatter dateFromString:intputDateStr];
	
	NSDateFormatter *outputDateFormatter = [[NSDateFormatter alloc] init];
	NSString *outputDateFormatterStr = @"z zz zzz zzzz";
	[outputDateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease]];
	[outputDateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"PST"]];
	[outputDateFormatter setDateFormat:outputDateFormatterStr];
	NSString *outputDateStr = [outputDateFormatter stringFromDate:inputDate];
	NSLog(@"|%@|%@ -> %@||", outputDateFormatterStr, intputDateStr, outputDateStr);
	[inputDateFormatter release];
	[outputDateFormatter release];

localeの設定は以下の部分で行っています。

	[outputDateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease]];

zzzz のときの文字列がアメリカ向けのもになっている事が確認できます。ちなみに日本に設定する場合は ja_JP となります。

フォーマットの指定 入力 出力
z zz zzz zzzz 2000/01/02 03:04:05 PST PST PST Pacific Standard Time

iPhoneの現在の設定に合わせる場合は以下の様に記述します。
iPhoneの設定を変える場合は「設定」アプリの「一般」-「言語環境」-「言語」。

	[outputDateFormatter setLocale:[NSLocale currentLocale]];

タイムゾーン

例えば以下の様なプログラムを書いた場合、LocaleはデフォルトではiPhoneに設定されているタイムゾーンが使用されます。

	NSDateFormatter *inputDateFormatter = [[NSDateFormatter alloc] init];
	NSString *inputDateStr = @"yyyy/MM/dd HH:mm:ss";
	[inputDateFormatter setDateFormat:inputDateStr];
	NSString *intputDateStr = @"2000/01/02 03:04:05";
	NSDate *inputDate = [inputDateFormatter dateFromString:intputDateStr];
	NSLog(@"%@ -> %@", inputDateStr, [inputDate description]);
	[inputDateFormatter release];

そのため日本の設定でこのプログラムを実行すると明示的な記述が無いタイムゾーンについては自動的に日本のタイムゾーンで有る、+9時間に設定されます。
iPhoneの設定を変える場合は「設定」アプリの「一般」-「日時と時刻」-「時間帯」。

2000/01/02 03:04:05 -> 2000-01-02 03:04:05 +0900

iPhoneの設定を「太平洋標準時(米国、パシフィカ/Pacifica, USA)」に変更した後にプログラムを実行すると以下の様に出力されます。

2000/01/02 03:04:05 -> 2000-01-02 03:04:05 -0800

太平洋標準時の-8時間が設定されているのが確認できます。

次にタイムゾーンを日本(日本、東京/Tokyo, Japan)に設定し、以下のプログラムを実行します。

	NSDateFormatter *inputDateFormatter = [[NSDateFormatter alloc] init];
	NSString *inputDateStr = @"yyyy/MM/dd HH:mm:ss Z";
	[inputDateFormatter setDateFormat:inputDateStr];
	NSString *intputDateStr = @"2000/01/02 03:04:05 +0800";
	NSDate *inputDate = [inputDateFormatter dateFromString:intputDateStr];
	NSLog(@"%@ -> %@", inputDateStr, [inputDate description]);
	[inputDateFormatter release];

この場合、以下の様になります。

yyyy/MM/dd HH:mm:ss Z -> 2000-01-02 04:04:05 +0900

入力の文字列に指定した時分秒は 03:04:05 ですがタイムゾーンが日本の+9時間より1時間過去である+8時間のタイムゾーンでの日時であるため+9時間のタイムゾーンへの変更が行われた結果、 04:04:05 と1時間進んだ時間に変更されています。

また、Localeの話のところで確認した様にLocaleの設定によりタイムゾーンの記述方法が異なります。

フォーマットの指定 Loacleの設定 入力 出力
z zz zzz zzzz 日本 2000/01/02 03:04:05 GMT-08:00 GMT-08:00 GMT-08:00 アメリカ太平洋標準時
z zz zzz zzzz アメリカ(太平洋標準時) 2000/01/02 03:04:05 PST PST PST Pacific Standard Time

そのためタイムゾーンを「アメリカ太平洋標準時」と指定する以下の様なプログラムを記述するとLocaleが米国の場合にパースが失敗し、結果が null(nil) と成ります。

	NSDateFormatter *inputDateFormatter = [[NSDateFormatter alloc] init];
	NSString *inputDateStr = @"yyyy/MM/dd HH:mm:ss zzzz";
	[inputDateFormatter setDateFormat:inputDateStr];
	NSString *intputDateStr = @"2000/01/02 03:04:05 アメリカ太平洋標準時";
	NSDate *inputDate;
	
	// Localeが日本
	[inputDateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"] autorelease]];
	inputDate = [inputDateFormatter dateFromString:intputDateStr];
	NSLog(@"[日本]%@ -> %@", inputDateStr, [inputDate description]);

	// Localeが米国
	[inputDateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease]];
	inputDate = [inputDateFormatter dateFromString:intputDateStr];
	NSLog(@"[米国]%@ -> %@", inputDateStr, [inputDate description]);

	[inputDateFormatter release];

出力結果は以下。

[日本]yyyy/MM/dd HH:mm:ss zzzz -> 2000-01-02 20:04:05 +0900
[米国]yyyy/MM/dd HH:mm:ss zzzz -> (null)

逆に「Pacific Standard Time」と指定するとLocaleが日本の時にパースに失敗します。

[日本]yyyy/MM/dd HH:mm:ss zzzz -> (null)
[米国]yyyy/MM/dd HH:mm:ss zzzz -> 2000-01-02 20:04:05 +0900

この様にLocaleにより、パースに影響が有りますので適切なLocaleを指定した上で dateFromString: を実行しましょう。

24時間設定をOFFの環境での注意点

iPhoneでは時間の表示をユーザが24時間表示と午前/午後の2つのパターンを選択できます。
※設定アプリの「一般」→「日付と時刻」に有る「24時間表示」で設定可能。

この「24時間表示」ですが、OFFのとき(つまり午前/午後表示の時)にはLocaleを設定していないとNSDateFormatterでHHを指定しても12時間表示での値が返ってくるようです。

Localeを指定した場合はHHでちゃんと24時間表示で返される様なのでLocaleは常に設定する様にしておきましょう。

詳しくはこちらのサイトに良くまとまっています。

日本語環境では、NSDateFormatterでフォーマットした日付がおかしい - 24/7 twenty-four seven
http://d.hatena.ne.jp/KishikawaKatsumi/20081121/1227275688

関連サイト

これで主な用途には問題ないとは思いますがその他のフォーマットについても知っておきたい人は以下のページなどを参照下さい。

Data Formatting Guide: Date Formatters
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html

NSDateFormatter
http://www.stepcase.com/blog/2008/12/02/format-string-for-the-iphone-nsdateformatter/

言語設定も意外と曲者なので以前、こちらのエントリーで解説しました。

iPhoneの言語設定関連の情報取得についての解説 - 強火で進め
http://d.hatena.ne.jp/nakamura001/20090511/1242061301

Cocoaの日々: [iOS][Mac] ISO 8601 相当の日付文字列を NSDateFormatter で変換する
http://cocoadays.blogspot.com/2011/03/iosmac-iso-8601-nsdateformatter.html