2010年3月10日水曜日

DateTimeFormatInfo.FullDateTimePattern プロパティについて

System.Globalization.CultureInfo クラス関連、Mono と MS.NET の違い

CultureInfo クラスでは、各カルチャ(ロケール)に対応した数値、日時などの書式(パターン)を管理していて、CultureInfo.DateTimeFormat プロパティで日時表示関連の情報 DateTimeFormatInfo を取得でき、更に、DateTimeFormatInfo.FullDateTimePattern プロパティで、標準の完全な日時表示書式(長い形式の日付と長い形式の時刻を合わせた書式で、書式文字列「"F"」に関連付けられている)を取得できる。

例えば「今日」を、標準の完全な日時表示書式で表す場合は、
  1. using System;  
  2. using System.Globalization;  
  3.   
  4. namespace CultureInfoExample  
  5. {  
  6.   public class Program  
  7.   {  
  8.     public static void Main(string[] args)  
  9.     {  
  10.       CultureInfo cci = CultureInfo.CurrentCulture;  
  11.       Console.WriteLine(" CULTURE NAME     FULL DATE TIME PATTERN");  
  12.       Console.WriteLine("=========================================");  
  13.       Console.WriteLine(" {0,-17}{1}", cci.Name, cci.DateTimeFormat.FullDateTimePattern);  
  14.       Console.WriteLine("\nTODAY : ");  
  15.       Console.WriteLine(" Standard Full    {0}", DateTime.Now.ToString("F"));  
  16.       Console.WriteLine(" Custom           {0}", DateTime.Now.ToString("yyyy/MM/dd (ddd) H:mm:ss"));  
  17.     }  
  18.   }  
  19. }  
  20. /* 
  21.  * ビルド: 
  22.  * 
  23.  *   gmcs cultureinfoexample01.cs 
  24.  * 
  25.  * 実行: 
  26.  * 
  27.  *   mono cultureinfoexample01.exe 
  28.  * 
  29.  */  
$ mono cultureinfoexample01.exe 
CULTURE NAME     FULL DATE TIME PATTERN
=========================================
ja-JP            yyyy'年'M'月'd'日' H:mm:ss

TODAY : 
Standard Full    2010年3月10日 22:51:21
Custom           2010/03/10 (水) 22:51:21

そして、次のコードを使用して、Mono 2.6.1(on Ubuntu 9.04)と .NET Framework 3.5 SP1(on Windows 7 RC)の各カルチャ標準の完全な日時表示書式を、出力結果で比較してみると
  1. using System;  
  2. using System.Globalization;  
  3.   
  4. namespace CultureInfoExample  
  5. {  
  6.   public class Program  
  7.   {  
  8.     public static void Main(string[] args)  
  9.     {  
  10.       string[] cultures = new [] {  
  11.         "",      // インバリアント カルチャ  
  12.         "af-ZA"// アフリカーンス語 (南アフリカ)  
  13.         "sq-AL"// アルバニア語 (アルバニア)  
  14.         "ar-DZ"// アラビア語 (アルジェリア)  
  15.         "ar-BH"// アラビア語 (バーレーン)  
  16.         "ar-EG"// アラビア語 (エジプト)  
  17.         "ar-IQ"// アラビア語 (イラク)  
  18.         "ar-JO"// アラビア語 (ヨルダン)  
  19.         "ar-KW"// アラビア語 (クウェート)  
  20.         "ar-LB"// アラビア語 (レバノン)  
  21.         "ar-LY"// アラビア語 (リビア)  
  22.         "ar-MA"// アラビア語 (モロッコ)  
  23.         "ar-OM"// アラビア語 (オマーン)  
  24.         "ar-QA"// アラビア語 (カタール)  
  25.         "ar-SA"// アラビア語 (サウジアラビア)  
  26.         "ar-SY"// アラビア語 (シリア)  
  27.         "ar-TN"// アラビア語 (チュニジア)  
  28.         "ar-AE"// アラビア語 (U.A.E.)  
  29.         "ar-YE"// アラビア語 (イエメン)  
  30.         "hy-AM"// アルメニア語 (アルメニア)  
  31.         "az-Cyrl-AZ"// アゼリ語 (アゼルバイジャン、キリル文字)  
  32.         "az-Latn-AZ"// アゼリ語 (アゼルバイジャン、ラテン文字)  
  33.         "eu-ES"// バスク語 (バスク)  
  34.         "be-BY"// ベラルーシ語 (ベラルーシ)  
  35.         "bg-BG"// ブルガリア語 (ブルガリア)  
  36.         "ca-ES"// カタロニア語 (カタルーニャ)  
  37.         "zh-HK"// 中国語 (香港特別行政区)  
  38.         "zh-MO"// 中国語 (マカオ)  
  39.         "zh-CN"// 中国語 (中国)  
  40.         "zh-Hans"// 簡体字中国語  
  41.         "zh-SG"// 中国語 (シンガポール)  
  42.         "zh-TW"// 中国語 (台湾)  
  43.         "zh-Hant"// 繁体字中国語  
  44.         "hr-HR"// クロアチア語 (クロアチア)  
  45.         "cs-CZ"// チェコ語 (チェコ共和国)  
  46.         "da-DK"// デンマーク語 (デンマーク)  
  47.         "dv-MV"// ディベヒ語 (モルジブ)  
  48.         "nl-BE"// オランダ語 (ベルギー)  
  49.         "nl-NL"// オランダ語 (オランダ)  
  50.         "en-AU"// 英語 (オーストラリア)  
  51.         "en-BZ"// 英語 (ベリーズ)  
  52.         "en-CA"// 英語 (カナダ)  
  53.         "en-029"// 英語 (西インド諸島)  
  54.         "en-IE"// 英語 (アイルランド)  
  55.         "en-JM"// 英語 (ジャマイカ)  
  56.         "en-NZ"// 英語 (ニュージーランド)  
  57.         "en-PH"// 英語 (フィリピン)  
  58.         "en-ZA"// 英語 (南アフリカ)  
  59.         "en-TT"// 英語 (トリニダードトバゴ)  
  60.         "en-GB"// 英語 (英国)  
  61.         "en-US"// 英語 (U.S.)  
  62.         "en-ZW"// 英語 (ジンバブエ)  
  63.         "et-EE"// エストニア語 (エストニア)  
  64.         "fo-FO"// フェロー語 (フェロー諸島)  
  65.         "fa-IR"// ペルシア語 (イラン)  
  66.         "fi-FI"// フィンランド語 (フィンランド)  
  67.         "fr-BE"// フランス語 (ベルギー)  
  68.         "fr-CA"// フランス語 (カナダ)  
  69.         "fr-FR"// フランス語 (フランス)  
  70.         "fr-LU"// フランス語 (ルクセンブルグ)  
  71.         "fr-MC"// フランス語 (モナコ)  
  72.         "fr-CH"// フランス語 (スイス)  
  73.         "gl-ES"// ガリシア語 (スペイン)  
  74.         "ka-GE"// グルジア語 (グルジア共和国)  
  75.         "de-AT"// ドイツ語 (オーストリア)  
  76.         "de-DE"// ドイツ語 (ドイツ)  
  77.         "de-LI"// ドイツ語 (リヒテンシュタイン)  
  78.         "de-LU"// ドイツ語 (ルクセンブルグ)  
  79.         "de-CH"// ドイツ語 (スイス)  
  80.         "el-GR"// ギリシア語 (ギリシア)  
  81.         "gu-IN"// グジャラート語 (インド)  
  82.         "he-IL"// ヘブライ語 (イスラエル)  
  83.         "hi-IN"// ヒンディー語 (インド)  
  84.         "hu-HU"// ハンガリー語 (ハンガリー)  
  85.         "is-IS"// アイスランド語 (アイスランド)  
  86.         "id-ID"// インドネシア語 (インドネシア)  
  87.         "it-IT"// イタリア語 (イタリア)  
  88.         "it-CH"// イタリア語 (スイス)  
  89.         "ja-JP"// 日本語 (日本)  
  90.         "kn-IN"// カナラ語 (インド)  
  91.         "kk-KZ"// カザフ語 (カザフスタン)  
  92.         "kok-IN"// コンカニ語 (インド)  
  93.         "ko-KR"// 韓国語 (韓国)  
  94.         "ky-KG"// キルギス語 (キルギス)  
  95.         "lv-LV"// ラトビア語 (ラトビア)  
  96.         "lt-LT"// リトアニア語 (リトアニア)  
  97.         "mk-MK"// マケドニア語 (マケドニア)  
  98.         "ms-BN"// マレー語 (ブルネイ ダルサラーム)  
  99.         "ms-MY"// マレー語 (マレーシア)  
  100.         "mr-IN"// マラティー語 (インド)  
  101.         "mn-MN"// モンゴル語 (モンゴル)  
  102.         "nb-NO"// ノルウェー語 (ブークモール、ノルウェー)  
  103.         "nn-NO"// ノルウェー語 (ニノーシク、ノルウェー)  
  104.         "pl-PL"// ポーランド語 (ポーランド)  
  105.         "pt-BR"// ポルトガル語 (ブラジル)  
  106.         "pt-PT"// ポルトガル語 (ポルトガル)  
  107.         "pa-IN"// パンジャブ語 (インド)  
  108.         "ro-RO"// ルーマニア語 (ルーマニア)  
  109.         "ru-RU"// ロシア語 (ロシア)  
  110.         "sa-IN"// サンスクリット語 (インド)  
  111.         "sr-Cyrl-CS"// セルビア語 (セルビア、キリル文字)  
  112.         "sr-Latn-CS"// セルビア語 (セルビア、ラテン文字)  
  113.         "sk-SK"// スロバキア語 (スロバキア)  
  114.         "sl-SI"// スロベニア語 (スロベニア)  
  115.         "es-AR"// スペイン語 (アルゼンチン)  
  116.         "es-BO"// スペイン語 (ボリビア)  
  117.         "es-CL"// スペイン語 (チリ)  
  118.         "es-CO"// スペイン語 (コロンビア)  
  119.         "es-CR"// スペイン語 (コスタリカ)  
  120.         "es-DO"// スペイン語 (ドミニカ共和国)  
  121.         "es-EC"// スペイン語 (エクアドル)  
  122.         "es-SV"// スペイン語 (エルサルバドル)  
  123.         "es-GT"// スペイン語 (グアテマラ)  
  124.         "es-HN"// スペイン語 (ホンジュラス)  
  125.         "es-MX"// スペイン語 (メキシコ)  
  126.         "es-NI"// スペイン語 (ニカラグア)  
  127.         "es-PA"// スペイン語 (パナマ)  
  128.         "es-PY"// スペイン語 (パラグアイ)  
  129.         "es-PE"// スペイン語 (ペルー)  
  130.         "es-PR"// スペイン語 (プエルトリコ)  
  131.         "es-ES"// スペイン語 (スペイン)  
  132.         "es-ES_tradnl"// スペイン語 (スペイン、従来の並べ替え順序)  
  133.         "es-UY"// スペイン語 (ウルグアイ)  
  134.         "es-VE"// スペイン語 (ベネズエラ)  
  135.         "sw-KE"// スワヒリ語 (ケニア)  
  136.         "sv-FI"// スウェーデン語 (フィンランド)  
  137.         "sv-SE"// スウェーデン語 (スウェーデン)  
  138.         "syr-SY"// シリア語 (シリア)  
  139.         "ta-IN"// タミル語 (インド)  
  140.         "tt-RU"// タタール語 (ロシア)  
  141.         "te-IN"// テルグ語 (インド)  
  142.         "th-TH"// タイ語 (タイ)  
  143.         "tr-TR"// トルコ語 (トルコ)  
  144.         "uk-UA"// ウクライナ語 (ウクライナ)  
  145.         "ur-PK"// ウルドゥー語 (パキスタン)  
  146.         "uz-Cyrl-UZ"// ウズベク語 (ウズベキスタン、キリル文字)  
  147.         "uz-Latn-UZ"// ウズベク語 (ウズベキスタン、ラテン文字)  
  148.         "vi-VN"  // ベトナム語 (ベトナム)        
  149.       };  
  150.   
  151.       string pattern = "";  
  152.   
  153.       Console.WriteLine(" CULTURE NAME     FULL DATE TIME PATTERN");  
  154.       Console.WriteLine("=========================================");  
  155.   
  156.       foreach (string culture in cultures)  
  157.       {  
  158.         try  
  159.         {  
  160.           pattern = CultureInfo.GetCultureInfo(culture).DateTimeFormat.FullDateTimePattern;  
  161.         }  
  162.         catch  
  163.         {  
  164.           pattern = "error";  
  165.         }  
  166.   
  167.         Console.WriteLine(" {0,-17}{1}", culture, pattern);  
  168.       }  
  169.     }  
  170.   }  
  171. }  
  172. /* 
  173.  * ビルド: 
  174.  * 
  175.  *   gmcs cultureinfoexample02.cs 
  176.  * 
  177.  * 実行: 
  178.  * 
  179.  *   mono cultureinfoexample02.exe 
  180.  * 
  181.  */  

おおお、けっこうな違いがある。

どうしてこんなことを調べたかというと、ASP.NET MVC 2 RC の単体テストの失敗ケースで、この差異に起因するものがあったからだ。


Mono では、CultureInfo のインスタンスの生成などは、Mono ランタイム内部の関数を呼び出して処理されており、各カルチャの数値、日時などの書式は、Mono ソース資源的には「/path/to/monosrc/mono/metadata/culture-info-table.h」でまとめられている。では、この「culture-info-table.h」を直接修正し、Mono ランタイムの再ビルドを行えばよいのかと言えば、「culture-info-table.h」自体はツールを使用して生成されており、「Do not edit.」と「culture-info-table.h」の頭に記述されている。

もちろん、その生成ツール(のソース)もソース資源に含まれており、「/path/to/monosrc/tools/locale-builder」配下に存在する。locale-builder の「README」によれば、「culture-info-table.h」のインプットとして「Unicode CLDR」(Unicode Releases Common Locale Data Repository version 1.7)を使用していて、MS.NET との差異を埋めるために「supplementalData.xml and supp/*.xml」以下の資源が用意されているということだ。

上記の、単体テストの失敗ケースの対応としては、カルチャ「es-PR」(スペイン語(プエルトリコ))の標準の完全な日時表示書式を「"d' de 'MMMM' de 'yyyy hh:mm:ss tt"」から「"dddd, dd' de 'MMMM' de 'yyyy hh:mm:ss tt"」に修正する必要があり(MS.NET に合致させるとしたら)、ならば、「supp/es_PR.xml」を修正し、生成ツールのビルド、実行をし、「culture-info-table.h」を再生成すればよいと考えられる。

しかし、そもそも MS.NET に合致させるのが正しいのだろうか?とか、「supp/*.xml」を具体的にどの様に修正したらいいのだろうか?とか、生成用の元ネタである「unicode CLDR」の資源をどのように配置して、生成ツールを実行すればいいのだろうか?とか、めんどくさいなど、基本的なことがよく分からないことに起因したり、しなかったりする、やりたくない理由がてんこ盛りなため、多分やらないwww。

0 件のコメント:

コメントを投稿