強火で進め

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

日付フォーマット yyyy と YYYY の違い

結論

まず最初に急いでる人向けに結論を先に書いておきます。2つの違いは以下の様に成っています。

yyyy

年(西暦)を出力

YYYY

ある年における「最初の木曜日を含む週が、その年の第1週である」というルールで年(西暦)を出力。
例えば 2015/1/1 は木曜日なのでその週の日は日曜日〜土曜日まで全て2015年の第1週という解釈になります。この場合には2014年で有る、 2014/12/28(日曜)〜2014/12/31(水曜) の時でも YYYY では 2015 を返します。

きっかけ

Podcast で Rebuild の第73回を聴いていたら日付フォーマットで yyyy ではなく、YYYY を使った為に Twitter の Android クライアントで不具合が出たという話が出てきました。
※根本的な原因はこのルールでサーバ側が実装されていた為、 Android クライアントで正しく認証処理が行われなかったという流れになります。自分の iPhone で主に使っている Twitter クライアントの Echofon でも同様に OAuth の認証に失敗するという症状が出ていました。

この症状が Objective-C ではどうなるのか以下の様なプログラムで検証してみました。
※まぁ、 Podcast の中では Objective-C でも同様に症状が出ると話されてましたが。

    NSDateFormatter *inputDateFormatter = [[NSDateFormatter alloc] init];
    [inputDateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss"];
    NSString *intputDateStr = @"2014/12/28 00:00:00";
    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(@"%@ -> %@", intputDateStr, outputDateStr);

※ Podcast では %G の話も出てきましたが Objective-C で G を使った場合には A.D や 西暦 などの文字列を返すという別のルールと成っているのでその辺りの検証処理は含まれていません。

プログラムの内容は 2014/12/28 00:00:00 を YYYY/MM/dd HH:mm:ss でパースするというものです。コレを実行した所、パース後のデータは 2015/12/28 00:00:00 。 Objective-C でも確かに同様の症状となる様です( Mac/iOS ともに同じ症状でした)。

因みに Objecitve-C ( NSDateFormatter ) で指定する日付フォーマットは ISO では無く、 Unicode のフォーマットが採用されています。

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

まぁ、 Unicode のフォーマットでも YYYY は ISO 8601 が採用されてるみたいですが。

UTS #35: Unicode LDML: Dates
http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Field_Symbol_Table

なお、 YYYY のルールはこちら。

ある年における「最初の木曜日を含む週が、その年の第1週である。」と規定されている。

※日曜日を週の始めとして処理。

ISO 8601 - Wikipedia
http://ja.wikipedia.org/wiki/ISO_8601#.E5.B9.B4.E3.81.A8.E9.80.B1.E3.81.A8.E6.9B.9C.E6.97.A5

yyyy を想定している時のテスト

自分が作ったプログラムがちゃんと yyyy の方のルールで動いているか確認する為には境界の値、水曜と木曜が1/1となる以下の2つの値をテストした時に年が次の翌年に変換されなければ良いでしょう。

  • 2013/12/31 00:00:00
  • 2014/12/31 00:00:00

※ 2014/1/1 が水曜日、 2015/1/1 が木曜日。

YYYY を想定している時のテスト

  • 2013/12/28 00:00:00
  • 2013/12/29 00:00:00

※ 2013/12/29 (日曜日)から2014年の第1週。なので 2013/12/28 は 2013 、 2013/12/29 は 2014 となれば正しく処理できています。

YYYY なんてどんな時に使うの?

YYYY はどんな時に使うの?という声が上がっていたので、こちらについても調べてみました。すると以下のサイトでヨーロッパでよく使われてるみたいだという事が判明しました。

ISO-8601 and related - SwedeTeam
http://www.swedeteam.com/iso8601/

例えば「00W09」として製造コードが記載されていれば 00W09 → 2000-W09 → 2000年の第9週目に製造されたものという表現がされるそうです。

この件とは関係ないですがこのページに記載されている MM-DD-YYYY という年月日は USA だけで使われているという話も中々興味深かったです。なんでこんな並びになっているのか、答えは「まったく理由無し。完全に非論理的」だそうですw

関連情報

If you're using YYYY in your JVM service or %G in anything, fix it now | Hacker News
https://news.ycombinator.com/item?id=8810157

Twitter skips most of 2015, locks users out of Android app | ZDNet
http://www.zdnet.com/article/twitter-skips-most-of-2015-locks-users-out-of-android-app/