2010年3月10日水曜日

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

このエントリーをはてなブックマークに追加
System.Globalization.CultureInfo クラス関連、Mono と MS.NET の違い

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

例えば「今日」を、標準の完全な日時表示書式で表す場合は、
using System;
using System.Globalization;

namespace CultureInfoExample
{
  public class Program
  {
    public static void Main(string[] args)
    {
      CultureInfo cci = CultureInfo.CurrentCulture;
      Console.WriteLine(" CULTURE NAME     FULL DATE TIME PATTERN");
      Console.WriteLine("=========================================");
      Console.WriteLine(" {0,-17}{1}", cci.Name, cci.DateTimeFormat.FullDateTimePattern);
      Console.WriteLine("\nTODAY : ");
      Console.WriteLine(" Standard Full    {0}", DateTime.Now.ToString("F"));
      Console.WriteLine(" Custom           {0}", DateTime.Now.ToString("yyyy/MM/dd (ddd) H:mm:ss"));
    }
  }
}
/*
 * ビルド:
 *
 *   gmcs cultureinfoexample01.cs
 *
 * 実行:
 *
 *   mono cultureinfoexample01.exe
 *
 */
$ 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)の各カルチャ標準の完全な日時表示書式を、出力結果で比較してみると
using System;
using System.Globalization;

namespace CultureInfoExample
{
  public class Program
  {
    public static void Main(string[] args)
    {
      string[] cultures = new [] {
        "",      // インバリアント カルチャ
        "af-ZA", // アフリカーンス語 (南アフリカ)
        "sq-AL", // アルバニア語 (アルバニア)
        "ar-DZ", // アラビア語 (アルジェリア)
        "ar-BH", // アラビア語 (バーレーン)
        "ar-EG", // アラビア語 (エジプト)
        "ar-IQ", // アラビア語 (イラク)
        "ar-JO", // アラビア語 (ヨルダン)
        "ar-KW", // アラビア語 (クウェート)
        "ar-LB", // アラビア語 (レバノン)
        "ar-LY", // アラビア語 (リビア)
        "ar-MA", // アラビア語 (モロッコ)
        "ar-OM", // アラビア語 (オマーン)
        "ar-QA", // アラビア語 (カタール)
        "ar-SA", // アラビア語 (サウジアラビア)
        "ar-SY", // アラビア語 (シリア)
        "ar-TN", // アラビア語 (チュニジア)
        "ar-AE", // アラビア語 (U.A.E.)
        "ar-YE", // アラビア語 (イエメン)
        "hy-AM", // アルメニア語 (アルメニア)
        "az-Cyrl-AZ", // アゼリ語 (アゼルバイジャン、キリル文字)
        "az-Latn-AZ", // アゼリ語 (アゼルバイジャン、ラテン文字)
        "eu-ES", // バスク語 (バスク)
        "be-BY", // ベラルーシ語 (ベラルーシ)
        "bg-BG", // ブルガリア語 (ブルガリア)
        "ca-ES", // カタロニア語 (カタルーニャ)
        "zh-HK", // 中国語 (香港特別行政区)
        "zh-MO", // 中国語 (マカオ)
        "zh-CN", // 中国語 (中国)
        "zh-Hans", // 簡体字中国語
        "zh-SG", // 中国語 (シンガポール)
        "zh-TW", // 中国語 (台湾)
        "zh-Hant", // 繁体字中国語
        "hr-HR", // クロアチア語 (クロアチア)
        "cs-CZ", // チェコ語 (チェコ共和国)
        "da-DK", // デンマーク語 (デンマーク)
        "dv-MV", // ディベヒ語 (モルジブ)
        "nl-BE", // オランダ語 (ベルギー)
        "nl-NL", // オランダ語 (オランダ)
        "en-AU", // 英語 (オーストラリア)
        "en-BZ", // 英語 (ベリーズ)
        "en-CA", // 英語 (カナダ)
        "en-029", // 英語 (西インド諸島)
        "en-IE", // 英語 (アイルランド)
        "en-JM", // 英語 (ジャマイカ)
        "en-NZ", // 英語 (ニュージーランド)
        "en-PH", // 英語 (フィリピン)
        "en-ZA", // 英語 (南アフリカ)
        "en-TT", // 英語 (トリニダードトバゴ)
        "en-GB", // 英語 (英国)
        "en-US", // 英語 (U.S.)
        "en-ZW", // 英語 (ジンバブエ)
        "et-EE", // エストニア語 (エストニア)
        "fo-FO", // フェロー語 (フェロー諸島)
        "fa-IR", // ペルシア語 (イラン)
        "fi-FI", // フィンランド語 (フィンランド)
        "fr-BE", // フランス語 (ベルギー)
        "fr-CA", // フランス語 (カナダ)
        "fr-FR", // フランス語 (フランス)
        "fr-LU", // フランス語 (ルクセンブルグ)
        "fr-MC", // フランス語 (モナコ)
        "fr-CH", // フランス語 (スイス)
        "gl-ES", // ガリシア語 (スペイン)
        "ka-GE", // グルジア語 (グルジア共和国)
        "de-AT", // ドイツ語 (オーストリア)
        "de-DE", // ドイツ語 (ドイツ)
        "de-LI", // ドイツ語 (リヒテンシュタイン)
        "de-LU", // ドイツ語 (ルクセンブルグ)
        "de-CH", // ドイツ語 (スイス)
        "el-GR", // ギリシア語 (ギリシア)
        "gu-IN", // グジャラート語 (インド)
        "he-IL", // ヘブライ語 (イスラエル)
        "hi-IN", // ヒンディー語 (インド)
        "hu-HU", // ハンガリー語 (ハンガリー)
        "is-IS", // アイスランド語 (アイスランド)
        "id-ID", // インドネシア語 (インドネシア)
        "it-IT", // イタリア語 (イタリア)
        "it-CH", // イタリア語 (スイス)
        "ja-JP", // 日本語 (日本)
        "kn-IN", // カナラ語 (インド)
        "kk-KZ", // カザフ語 (カザフスタン)
        "kok-IN", // コンカニ語 (インド)
        "ko-KR", // 韓国語 (韓国)
        "ky-KG", // キルギス語 (キルギス)
        "lv-LV", // ラトビア語 (ラトビア)
        "lt-LT", // リトアニア語 (リトアニア)
        "mk-MK", // マケドニア語 (マケドニア)
        "ms-BN", // マレー語 (ブルネイ ダルサラーム)
        "ms-MY", // マレー語 (マレーシア)
        "mr-IN", // マラティー語 (インド)
        "mn-MN", // モンゴル語 (モンゴル)
        "nb-NO", // ノルウェー語 (ブークモール、ノルウェー)
        "nn-NO", // ノルウェー語 (ニノーシク、ノルウェー)
        "pl-PL", // ポーランド語 (ポーランド)
        "pt-BR", // ポルトガル語 (ブラジル)
        "pt-PT", // ポルトガル語 (ポルトガル)
        "pa-IN", // パンジャブ語 (インド)
        "ro-RO", // ルーマニア語 (ルーマニア)
        "ru-RU", // ロシア語 (ロシア)
        "sa-IN", // サンスクリット語 (インド)
        "sr-Cyrl-CS", // セルビア語 (セルビア、キリル文字)
        "sr-Latn-CS", // セルビア語 (セルビア、ラテン文字)
        "sk-SK", // スロバキア語 (スロバキア)
        "sl-SI", // スロベニア語 (スロベニア)
        "es-AR", // スペイン語 (アルゼンチン)
        "es-BO", // スペイン語 (ボリビア)
        "es-CL", // スペイン語 (チリ)
        "es-CO", // スペイン語 (コロンビア)
        "es-CR", // スペイン語 (コスタリカ)
        "es-DO", // スペイン語 (ドミニカ共和国)
        "es-EC", // スペイン語 (エクアドル)
        "es-SV", // スペイン語 (エルサルバドル)
        "es-GT", // スペイン語 (グアテマラ)
        "es-HN", // スペイン語 (ホンジュラス)
        "es-MX", // スペイン語 (メキシコ)
        "es-NI", // スペイン語 (ニカラグア)
        "es-PA", // スペイン語 (パナマ)
        "es-PY", // スペイン語 (パラグアイ)
        "es-PE", // スペイン語 (ペルー)
        "es-PR", // スペイン語 (プエルトリコ)
        "es-ES", // スペイン語 (スペイン)
        "es-ES_tradnl", // スペイン語 (スペイン、従来の並べ替え順序)
        "es-UY", // スペイン語 (ウルグアイ)
        "es-VE", // スペイン語 (ベネズエラ)
        "sw-KE", // スワヒリ語 (ケニア)
        "sv-FI", // スウェーデン語 (フィンランド)
        "sv-SE", // スウェーデン語 (スウェーデン)
        "syr-SY", // シリア語 (シリア)
        "ta-IN", // タミル語 (インド)
        "tt-RU", // タタール語 (ロシア)
        "te-IN", // テルグ語 (インド)
        "th-TH", // タイ語 (タイ)
        "tr-TR", // トルコ語 (トルコ)
        "uk-UA", // ウクライナ語 (ウクライナ)
        "ur-PK", // ウルドゥー語 (パキスタン)
        "uz-Cyrl-UZ", // ウズベク語 (ウズベキスタン、キリル文字)
        "uz-Latn-UZ", // ウズベク語 (ウズベキスタン、ラテン文字)
        "vi-VN"  // ベトナム語 (ベトナム)      
      };

      string pattern = "";

      Console.WriteLine(" CULTURE NAME     FULL DATE TIME PATTERN");
      Console.WriteLine("=========================================");

      foreach (string culture in cultures)
      {
        try
        {
          pattern = CultureInfo.GetCultureInfo(culture).DateTimeFormat.FullDateTimePattern;
        }
        catch
        {
          pattern = "error";
        }

        Console.WriteLine(" {0,-17}{1}", culture, pattern);
      }
    }
  }
}
/*
 * ビルド:
 *
 *   gmcs cultureinfoexample02.cs
 *
 * 実行:
 *
 *   mono cultureinfoexample02.exe
 *
 */

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

どうしてこんなことを調べたかというと、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 件のコメント:

コメントを投稿