ここではカルチャに設定される固有の書式・テキスト処理の規則と暦について見ていきます。

ブラウザで使用しているフォントによっては、この文章の一部で正しく表示されない文字があるかもしれません。 実行結果についても、実行環境によって異なる結果となる場合がある点に注意してください。

§1 カルチャと書式

ニュートラルでないカルチャには、日時や数値に固有の書式が割り当てられています。 この書式は、カルチャの言語・国・地域にあわせて桁の区切・月の名前・曜日名・通貨記号などの文字列がローカライズされた表記・形式となっています。 これらの書式は、数値や日付・時間などの値を文字列化する場合などに使用されます。

§1.1 スレッドのカルチャと書式

ToStringなどのメソッドでは、書式を指定しなかった場合やカルチャに依存する形式に書式化される書式指定文字列を使用した場合は、現在のスレッドのカルチャに従ってローカライズされた形式で文字列化されます。 そのため、スレッドのカルチャを変更するとこれらのメソッドの結果にも影響します。

次のコードでは、スレッドのカルチャを変更して、書式を指定しない場合とローカライズされる書式指定文字列を使った場合の文字列化の結果を出力しています。

using System;
using System.Globalization;
using System.Threading;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("en-GB"),
      CultureInfo.GetCultureInfo("fr-FR"),
      CultureInfo.GetCultureInfo("es-ES"),
      CultureInfo.InvariantCulture,
    };

    // decimal型の値をいくつかの書式・カルチャの組み合わせで文字列化
    Console.WriteLine("number");

    decimal number = -12345.67m;

    foreach (CultureInfo culture in cultures) {
      Thread.CurrentThread.CurrentCulture = culture;

      Console.WriteLine("{0,-6}| {1,-15} {2,-15} {3,-15}",
                        CultureInfo.CurrentCulture.Name,
                        number.ToString(),
                        number.ToString("N"),
                        number.ToString("C"));
    }

    // DateTime型の値をいくつかの書式・カルチャの組み合わせで文字列化
    Console.WriteLine("date and time");

    DateTime dateTime = new DateTime(2000, 1, 23, 4, 5, 6);

    foreach (CultureInfo culture in cultures) {
      Thread.CurrentThread.CurrentCulture = culture;

      Console.WriteLine("{0,-6}| {1,-25} {2,-15} {3}",
                        CultureInfo.CurrentCulture.Name,
                        dateTime.ToString(),
                        dateTime.ToString("T"),
                        dateTime.ToString("F"));
    }
  }
}
実行結果
number
ja-JP | -12345.67       -12,345.67      -\12,346       
en-US | -12345.67       -12,345.67      ($12,345.67)   
en-GB | -12345.67       -12,345.67      -£12,345.67    
fr-FR | -12345,67       -12 345,67      -12 345,67 €   
es-ES | -12345,67       -12.345,67      -12.345,67 €   
      | -12345.67       -12,345.67      ($12,345.67)   
date and time
ja-JP | 2000/01/23 4:05:06        4:05:06         2000年1月23日 4:05:06
en-US | 1/23/2000 4:05:06 AM      4:05:06 AM      Sunday, January 23, 2000 4:05:06 AM
en-GB | 23/01/2000 04:05:06       04:05:06        23 January 2000 04:05:06
fr-FR | 23/01/2000 04:05:06       04:05:06        dimanche 23 janvier 2000 04:05:06
es-ES | 23/01/2000 4:05:06        4:05:06         domingo, 23 de enero de 2000 4:05:06
      | 01/23/2000 04:05:06       04:05:06        Sunday, 23 January 2000 04:05:06

このように、スレッドのカルチャに応じて、桁区切り記号や小数点記号・通貨記号、月名・曜日名がローカライズされて文字列化されます。

§1.2 カルチャと書式プロバイダ

ToStringなどのメソッドは、引数にCultureInfoを指定することが出来ます。 スレッドのカルチャを変更する代わりにToStringメソッドにCultureInfoを指定することで、そのカルチャの書式を使うように指定することが出来ます。

次のコードでは、先のコードを少し変更し、スレッドのカルチャは変更せずにToStringメソッドの引数にCultureInfoを指定した場合の文字列化の結果を出力しています。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("en-GB"),
      CultureInfo.GetCultureInfo("fr-FR"),
      CultureInfo.GetCultureInfo("es-ES"),
      CultureInfo.InvariantCulture,
    };

    // decimal型の値をいくつかの書式・カルチャの組み合わせで文字列化
    Console.WriteLine("number");

    decimal number = -12345.67m;

    foreach (CultureInfo culture in cultures) {
      Console.WriteLine("{0,-6} {1,-15} | {2,-6} {3,-15} {4,-15} {5,-15}",
                        CultureInfo.CurrentCulture.Name,
                        number.ToString(),
                        culture.Name,
                        number.ToString(culture),
                        number.ToString("N", culture),
                        number.ToString("C", culture));
    }

    // DateTime型の値をいくつかの書式・カルチャの組み合わせで文字列化
    Console.WriteLine("date and time");

    DateTime dateTime = new DateTime(2000, 1, 23, 4, 5, 6);

    foreach (CultureInfo culture in cultures) {
      Console.WriteLine("{0,-6} {1,-15} | {2,-6} {3,-25} {4,-15} {5}",
                        CultureInfo.CurrentCulture.Name,
                        dateTime.ToString(),
                        culture.Name,
                        dateTime.ToString(culture),
                        dateTime.ToString("T", culture),
                        dateTime.ToString("F", culture));
    }
  }
}
実行結果
number
ja-JP  -12345.67       | ja-JP  -12345.67       -12,345.67      -\12,346       
ja-JP  -12345.67       | en-US  -12345.67       -12,345.67      ($12,345.67)   
ja-JP  -12345.67       | en-GB  -12345.67       -12,345.67      -£12,345.67    
ja-JP  -12345.67       | fr-FR  -12345,67       -12 345,67      -12 345,67 €   
ja-JP  -12345.67       | es-ES  -12345,67       -12.345,67      -12.345,67 €   
ja-JP  -12345.67       |        -12345.67       -12,345.67      ($12,345.67)   
date and time
ja-JP  2000/01/23 4:05:06 | ja-JP  2000/01/23 4:05:06        4:05:06         2000年1月23日 4:05:06
ja-JP  2000/01/23 4:05:06 | en-US  1/23/2000 4:05:06 AM      4:05:06 AM      Sunday, January 23, 2000 4:05:06 AM
ja-JP  2000/01/23 4:05:06 | en-GB  23/01/2000 04:05:06       04:05:06        23 January 2000 04:05:06
ja-JP  2000/01/23 4:05:06 | fr-FR  23/01/2000 04:05:06       04:05:06        dimanche 23 janvier 2000 04:05:06
ja-JP  2000/01/23 4:05:06 | es-ES  23/01/2000 4:05:06        4:05:06         domingo, 23 de enero de 2000 4:05:06
ja-JP  2000/01/23 4:05:06 |        01/23/2000 04:05:06       04:05:06        Sunday, 23 January 2000 04:05:06

このように、ToStringメソッドの引数にCultureInfoを指定した場合、(スレッドのカルチャとは無関係に)指定されたカルチャに固有な形式へとローカライズされた文字列に変換されます。

この例で使用したToStringメソッドのオーバーロードは、IFormatProviderインターフェイスを引数に取るようになっています。 これは書式プロバイダと呼ばれるもので、数値や日付・時間などを文字列化するときに使用する書式を参照する際に呼び出されます。 CultureInfoクラスはIFormatProviderインターフェイスを実装しているため、ToStringメソッドに渡すことが出来ます。 この場合、CultureInfo自身が書式プロバイダとして機能し、ローカライズされた形式の書式を提供するようになります。

このときに使用される具体的な書式は、数値ならCultureInfo.NumberFormatプロパティで取得できるNumberFormatInfoクラス、日付と時間の場合はCultureInfo.DateTimeFormatプロパティで取得できるDateTimeFormatInfoクラスを使って決定されます。

ToString等のメソッドと書式プロバイダ・IFormatProviderインターフェイスの関係については書式の定義と実装で解説しているので、独自に実装する場合やカスタマイズする場合などはそちらを参照してください。

§1.3 数値の書式 (NumberFormatInfo)

NumberFormatInfoクラスは、桁区切り・小数点・通貨単位など数値に関する記号や書式化のルールを提供するクラスで、特定のカルチャにローカライズされた形式で取得することが出来ます。

このクラスのコンストラクタでは特定の言語や国・地域を指定することは出来ず、インバリアントなインスタンスしか作成出来ません。 代わりに、ニュートラルでないカルチャのCultureInfo.NumberFormatプロパティから、カルチャに対応するNumberFormatInfoを取得することが出来ます。

次のコードでは、いくつかのカルチャでのNumberFormatInfoを取得し、NumberFormatInfoに設定されているプロパティの内容を表示しています。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("en-GB"),
      CultureInfo.GetCultureInfo("fr-FR"),
      CultureInfo.GetCultureInfo("es-ES"),
      CultureInfo.InvariantCulture,
    };

    foreach (CultureInfo culture in cultures) {
      NumberFormatInfo nf = culture.NumberFormat;

      Console.WriteLine("{0,-6}|{1,-3} {2,-3} {3,-3} {4,-3} {5,-3} {6,-3} {7,-3}",
                        culture.Name,
                        nf.NumberDecimalSeparator,
                        nf.NumberGroupSeparator,
                        nf.NumberGroupSizes[0],
                        nf.NumberNegativePattern,
                        nf.CurrencySymbol,
                        nf.CurrencyPositivePattern,
                        nf.PositiveInfinitySymbol);
    }
  }
}
実行結果
ja-JP |.   ,   3   1   ¥   0   +∞ 
en-US |.   ,   3   1   $   0   Infinity
en-GB |.   ,   3   1   £   0   Infinity
fr-FR |,       3   1   €   3   +Infini
es-ES |,   .   3   1   €   3   Infinito
      |.   ,   3   1   ¤   0   Infinity

NumberFormatInfoのプロパティを設定することで、書式を変えることも出来ます。 例えば、桁区切りを4桁毎(万単位)にしたり、通貨記号や小数点・桁区切りの記号を全角にしたりすることが出来ます。 以下の例では、NumberFormatInfoのプロパティを変更して書式をカスタマイズしています。 なお、比較のためにカスタマイズしていないデフォルトの書式も併記しています。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    // 文字列化する数値
    decimal positiveNumber = +123456.7890m;
    decimal negativeNumber = -123456.7890m;

    // デフォルトのja-JPのカルチャを作成 (読み取り専用)
    CultureInfo jajpDefault = CultureInfo.GetCultureInfo("ja-JP");

    // カスタマイズするja-JP用のカルチャを作成
    CultureInfo jajpCustom = new CultureInfo("ja-JP");

    // NumberFormatInfoを取得
    NumberFormatInfo nf = jajpCustom.NumberFormat;

    nf.NumberGroupSizes = new int[] {4}; // 一般数値の桁区切りを4桁にする
    nf.NumberDecimalSeparator = ".";    // 一般数値の小数点区切りを全角にする
    nf.NumberGroupSeparator = ",";      // 一般数値の桁区切りを全角にする

    Console.WriteLine("number");
    Console.WriteLine("{0,-20} {1,-20}", positiveNumber.ToString(jajpDefault), positiveNumber.ToString(jajpCustom));
    Console.WriteLine("{0,-20} {1,-20}", negativeNumber.ToString(jajpDefault), negativeNumber.ToString(jajpCustom));
    Console.WriteLine("{0,-20} {1,-20}", positiveNumber.ToString("N4", jajpDefault), positiveNumber.ToString("N4", jajpCustom));
    Console.WriteLine("{0,-20} {1,-20}", negativeNumber.ToString("N4", jajpDefault), negativeNumber.ToString("N4", jajpCustom));

    nf.NegativeSign = "マイナス";  // 負の値を文字で表記する

    nf.CurrencyGroupSeparator = " ";  // 通貨の桁区切り記号を空白にする
    nf.CurrencySymbol = "円";         // 通貨記号を「円」にする
    nf.CurrencyPositivePattern = 1;   // 正の通貨の表記をn$のパターンにする
    nf.CurrencyNegativePattern = 5;   // 負の通貨の表記を-n$のパターンにする

    Console.WriteLine("currency");
    Console.WriteLine("{0,-20} {1,-20}", positiveNumber.ToString("C4", jajpDefault), positiveNumber.ToString("C4", jajpCustom));
    Console.WriteLine("{0,-20} {1,-20}", negativeNumber.ToString("C4", jajpDefault), negativeNumber.ToString("C4", jajpCustom));

    nf.PercentSymbol = "\u332b";      // パーセント記号を「㌫」(U+332B)にする
    nf.PercentGroupSeparator = "";    // パーセント値の桁区切りをなくす

    Console.WriteLine("percent");
    Console.WriteLine("{0,-20} {1,-20}", positiveNumber.ToString("P", jajpDefault), positiveNumber.ToString("P", jajpCustom));
    Console.WriteLine("{0,-20} {1,-20}", negativeNumber.ToString("P", jajpDefault), negativeNumber.ToString("P", jajpCustom));
  }
}
実行結果
number
123456.789           123456.789          
-123456.789          -123456.789         
123,456.7890         12,3456.7890        
-123,456.7890        -12,3456.7890       
currency
¥123,456.7890        123 456.7890円       
-¥123,456.7890       マイナス123 456.7890円   
percent
12,345,678.90%       12345678.90㌫        
-12,345,678.90%      マイナス12345678.90㌫    

§1.4 日付と時間の書式 (DateTimeFormatInfo)

DateTimeFormatInfoクラスは、日付・時刻・午前/午後や曜日名・月名など日付と時刻に関する表記と書式化のルールを提供するクラスで、特定のカルチャにローカライズされた形式で取得することが出来ます。

NumberFormatInfoと同様、このクラスのコンストラクタでは特定の言語や国・地域を指定することは出来ず、インバリアントなインスタンスしか作成出来ません。 代わりに、ニュートラルでないカルチャのCultureInfo.DateTimeFormatプロパティから、カルチャに対応するDateTimeFormatInfoを取得することが出来ます。

次のコードでは、いくつかのカルチャでのDateTimeFormatInfoを取得し、DateTimeFormatInfoに設定されているプロパティの内容を表示しています。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("en-GB"),
      CultureInfo.GetCultureInfo("fr-FR"),
      CultureInfo.GetCultureInfo("es-ES"),
      CultureInfo.InvariantCulture,
    };

    foreach (CultureInfo culture in cultures) {
      DateTimeFormatInfo dtf = culture.DateTimeFormat;

      Console.WriteLine("{0,-6}|{1,-40} {2,-15} {3,-15} {4,-10} {5,-10} {6} {7}",
                        culture.Name,
                        dtf.FullDateTimePattern,
                        dtf.ShortDatePattern,
                        dtf.ShortTimePattern,
                        dtf.FirstDayOfWeek,
                        dtf.CalendarWeekRule,
                        dtf.AMDesignator,
                        dtf.PMDesignator);
    }

    Console.WriteLine("[MonthNames]");

    foreach (CultureInfo culture in cultures) {
      Console.WriteLine("{0,-6}|{1}", culture.Name, string.Join(", ", culture.DateTimeFormat.MonthNames));
    }

    Console.WriteLine("[DayNames]");

    foreach (CultureInfo culture in cultures) {
      Console.WriteLine("{0,-6}|{1}", culture.Name, string.Join(", ", culture.DateTimeFormat.DayNames));
    }

    Console.WriteLine("[AbbreviatedDayNames]");

    foreach (CultureInfo culture in cultures) {
      Console.WriteLine("{0,-6}|{1}", culture.Name, string.Join(", ", culture.DateTimeFormat.AbbreviatedDayNames));
    }
  }
}
実行結果
ja-JP |yyyy'年'M'月'd'日' H:mm:ss                  yyyy/MM/dd      H:mm            Sunday     FirstDay   午前 午後
en-US |dddd, MMMM dd, yyyy h:mm:ss tt           M/d/yyyy        h:mm tt         Sunday     FirstDay   AM PM
en-GB |dd MMMM yyyy HH:mm:ss                    dd/MM/yyyy      HH:mm           Monday     FirstDay   AM PM
fr-FR |dddd d MMMM yyyy HH:mm:ss                dd/MM/yyyy      HH:mm           Monday     FirstDay    
es-ES |dddd, dd' de 'MMMM' de 'yyyy H:mm:ss     dd/MM/yyyy      H:mm            Monday     FirstDay    
      |dddd, dd MMMM yyyy HH:mm:ss              MM/dd/yyyy      HH:mm           Sunday     FirstDay   AM PM
[MonthNames]
ja-JP |1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月, 
en-US |January, February, March, April, May, June, July, August, September, October, November, December, 
en-GB |January, February, March, April, May, June, July, August, September, October, November, December, 
fr-FR |janvier, février, mars, avril, mai, juin, juillet, août, septembre, octobre, novembre, décembre, 
es-ES |enero, febrero, marzo, abril, mayo, junio, julio, agosto, septiembre, octubre, noviembre, diciembre, 
      |January, February, March, April, May, June, July, August, September, October, November, December, 
[DayNames]
ja-JP |日曜日, 月曜日, 火曜日, 水曜日, 木曜日, 金曜日, 土曜日
en-US |Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
en-GB |Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
fr-FR |dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi
es-ES |domingo, lunes, martes, miércoles, jueves, viernes, sábado
      |Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
[AbbreviatedDayNames]
ja-JP |日, 月, 火, 水, 木, 金, 土
en-US |Sun, Mon, Tue, Wed, Thu, Fri, Sat
en-GB |Sun, Mon, Tue, Wed, Thu, Fri, Sat
fr-FR |dim., lun., mar., mer., jeu., ven., sam.
es-ES |dom, lun, mar, mié, jue, vie, sáb
      |Sun, Mon, Tue, Wed, Thu, Fri, Sat

NumberFormatInfoと同様、DateTimeFormatInfoのプロパティを変更して書式をカスタマイズすることが出来ます。 以下の例では、月名をカスタマイズしています。 なお、比較のためにカスタマイズしていないデフォルトの書式も併記しています。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    // デフォルトのja-JPのカルチャを作成 (読み取り専用)
    CultureInfo jajpDefault = CultureInfo.GetCultureInfo("ja-JP");

    // カスタマイズするja-JP用のカルチャを作成
    CultureInfo jajpCustom = new CultureInfo("ja-JP");

    // DateTimeFormatInfoを取得
    DateTimeFormatInfo dtf = jajpCustom.DateTimeFormat;

    // 月名を変更
    dtf.MonthNames = new string[] {
      "睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走", string.Empty,
    };

    for (DateTime dateTime = new DateTime(2010, 1, 1); dateTime.Year == 2010; dateTime = dateTime.AddDays(29).AddMinutes(108)) {
      Console.WriteLine("{0,-25} {1,-25}", dateTime.ToString("F", jajpDefault), dateTime.ToString("F", jajpCustom));
    }

    // 書式指定文字"F"に関連付けられている書式を変更
    // (DateTimeFormatInfo.MonthNamesは、書式指定文字MMMMを置換する際に使用される)
    dtf.FullDateTimePattern = "西暦yyyy年 MMMM dd日 (dddd) tthh時 mm分 ss秒";

    for (DateTime dateTime = new DateTime(2010, 1, 1); dateTime.Year == 2010; dateTime = dateTime.AddDays(29).AddMinutes(108)) {
      Console.WriteLine("{0,-25} {1,-25}", dateTime.ToString("F", jajpDefault), dateTime.ToString("F", jajpCustom));
    }
  }
}
実行結果
2010年1月1日 0:00:00         2010年1月1日 0:00:00        
2010年1月30日 1:48:00        2010年1月30日 1:48:00       
2010年2月28日 3:36:00        2010年2月28日 3:36:00       
2010年3月29日 5:24:00        2010年3月29日 5:24:00       
2010年4月27日 7:12:00        2010年4月27日 7:12:00       
2010年5月26日 9:00:00        2010年5月26日 9:00:00       
2010年6月24日 10:48:00       2010年6月24日 10:48:00      
2010年7月23日 12:36:00       2010年7月23日 12:36:00      
2010年8月21日 14:24:00       2010年8月21日 14:24:00      
2010年9月19日 16:12:00       2010年9月19日 16:12:00      
2010年10月18日 18:00:00      2010年10月18日 18:00:00     
2010年11月16日 19:48:00      2010年11月16日 19:48:00     
2010年12月15日 21:36:00      2010年12月15日 21:36:00     
2010年1月1日 0:00:00         西暦2010年 睦月 01日 (金曜日) 午前12時 00分 00秒
2010年1月30日 1:48:00        西暦2010年 睦月 30日 (土曜日) 午前01時 48分 00秒
2010年2月28日 3:36:00        西暦2010年 如月 28日 (日曜日) 午前03時 36分 00秒
2010年3月29日 5:24:00        西暦2010年 弥生 29日 (月曜日) 午前05時 24分 00秒
2010年4月27日 7:12:00        西暦2010年 卯月 27日 (火曜日) 午前07時 12分 00秒
2010年5月26日 9:00:00        西暦2010年 皐月 26日 (水曜日) 午前09時 00分 00秒
2010年6月24日 10:48:00       西暦2010年 水無月 24日 (木曜日) 午前10時 48分 00秒
2010年7月23日 12:36:00       西暦2010年 文月 23日 (金曜日) 午後12時 36分 00秒
2010年8月21日 14:24:00       西暦2010年 葉月 21日 (土曜日) 午後02時 24分 00秒
2010年9月19日 16:12:00       西暦2010年 長月 19日 (日曜日) 午後04時 12分 00秒
2010年10月18日 18:00:00      西暦2010年 神無月 18日 (月曜日) 午後06時 00分 00秒
2010年11月16日 19:48:00      西暦2010年 霜月 16日 (火曜日) 午後07時 48分 00秒
2010年12月15日 21:36:00      西暦2010年 師走 15日 (水曜日) 午後09時 36分 00秒

ほとんど(すべて?)のカルチャでは、デフォルトでグレゴリオ暦が使用されます。 使用する暦を変更する必要がある場合は、DateTimeFormatInfo.Calendarプロパティを変更します。 また、月名は第十三月が存在する暦に対応するため要素が13個の配列を指定する必要があります。 暦(Calendar)については後ほど詳しく解説します。



§2 カルチャとテキスト処理

書式と同様、ニュートラルでないカルチャには固有の文字列比較・変換の規則が割り当てられています。 例えば、大文字小文字の変換規則や、文字の並べ替えの規則などです。

§2.1 スレッドのカルチャとテキスト処理

Array.Sortなどのメソッドで文字列を比較する場合、String.Compareメソッドが呼び出されます。 String.Compareにオプションを指定しなかった場合のデフォルトの動作では、現在のスレッドのカルチャに固有な規則に基づいて比較が行われます。 同様に、String.ToUpperやString.ToLowerなどのメソッドでもカルチャ固有の規則で変換されます。 そのため、スレッドのカルチャを変更するとこれらのメソッドの結果にも影響します。

次のコードでは、スレッドのカルチャを変更し、Array.Sortメソッドを使って文字列の配列をソートした場合の結果を出力しています。 なお、この例で使用しているCultureInfo.TextInfo.ListSeparatorはカルチャ固有のリスト区切り文字を取得するプロパティです。

using System;
using System.Globalization;
using System.Threading;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("en-GB"),
      CultureInfo.GetCultureInfo("fr-FR"),
      CultureInfo.GetCultureInfo("es-ES"),
      CultureInfo.InvariantCulture,
    };

    string[] words1 = new string[] {"coté", "côte", "cote"};
    string[] words2 = new string[] {"亜", "井", "宇"};

    foreach (CultureInfo culture in cultures) {
      Thread.CurrentThread.CurrentCulture = culture;

      Array.Sort(words1);
      Array.Sort(words2);

      string separator = CultureInfo.CurrentCulture.TextInfo.ListSeparator;

      Console.WriteLine("{0,-6}| {1,-20} {2,-20}",
                        CultureInfo.CurrentCulture.Name,
                        string.Join(separator, words1),
                        string.Join(separator, words2));
    }
  }
}
実行結果
ja-JP | cote,coté,côte       亜,井,宇               
en-US | cote,coté,côte       井,亜,宇               
en-GB | cote,coté,côte       井,亜,宇               
fr-FR | cote;côte;coté       井;亜;宇               
es-ES | cote;coté;côte       井;亜;宇               
      | cote,coté,côte       井,亜,宇  

§2.2 文字列の比較 (CompareInfo)

CompareInfoクラスは、文字列の並べ替え(大小関係)と等価性の規則を提供するクラスで、特定のカルチャでの規則に基づいた文字列の検索・比較を行うことが出来ます。

このクラスのインスタンスを作成するには、GetCompareInfoメソッドでカルチャの名前もしくはIDを指定するか、CultureInfo.CompareInfoプロパティからカルチャに対応するCompareInfoを取得することが出来ます。

次のコードでは、いくつかのカルチャでのCompareInfoを取得し、CompareInfo.Compareメソッドを使って文字列の比較を行っています。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("en-GB"),
      CultureInfo.GetCultureInfo("fr-FR"),
      CultureInfo.GetCultureInfo("es-ES"),
      CultureInfo.InvariantCulture,
    };

    string[] pair1 = new string[] {"coté", "côte"};
    string[] pair2 = new string[] {"亜", "井"};
    string[] pair3 = new string[] {"abc", "ABC"};

    Console.WriteLine("{0,-6}| {1,-14}| {2,-14}| {3,-14}",
                      "code",
                      pair1[0] + "<=>" + pair1[1],
                      pair2[0] + "<=>" + pair2[1],
                      pair3[0] + "<=>" + pair3[1]);

    Console.WriteLine(new string('-', 60));

    foreach (CultureInfo culture in cultures) {
      CompareInfo ci = culture.CompareInfo;

      Console.WriteLine("{0,-6}| {1,-14}| {2,-14}| {3,-14}",
                        culture.Name,
                        ci.Compare(pair1[0], pair1[1]),
                        ci.Compare(pair2[0], pair2[1]),
                        ci.Compare(pair3[0], pair3[1], CompareOptions.IgnoreCase | CompareOptions.IgnoreWidth));
    }
  }
}
実行結果
code  | coté<=>côte   | 亜<=>井         | abc<=>ABC     
------------------------------------------------------------
ja-JP | -1            | -1            | 0             
en-US | -1            | 1             | 0             
en-GB | -1            | 1             | 0             
fr-FR | 1             | 1             | 0             
es-ES | -1            | 1             | 0             
      | -1            | 1             | 0             

なお、CompareInfoやCompareOptionsを指定した文字列の検索・比較は文字列と比較オプション・カルチャの並べ替え規則でも詳しく解説しています。

§2.3 文字列の変換 (TextInfo)

TextInfoクラスは、文字列の変換の規則を提供するクラスで、特定のカルチャでの規則に基づいた大文字小文字の変換やコードページの取得などを行うことが出来ます。

このクラスのコンストラクタは公開されていないため、カルチャに対応するTextInfoのインスタンスを取得するには、CultureInfo.TextInfoプロパティを参照します。

次のコードでは、いくつかのカルチャでのTextInfoを取得し、TextInfoのメソッドを使って文字列を大文字化・小文字化・タイトルケース化しています。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("tr-TR"),
      CultureInfo.InvariantCulture,
    };

    string text = "tHe inTERnet";

    Console.WriteLine("{0,-6}| {1,-15}| {2,-15}| {3,-15}",
                      "code",
                      "ToUpper",
                      "ToLower",
                      "ToTitleCase");

    Console.WriteLine(new string('-', 60));

    foreach (CultureInfo culture in cultures) {
      TextInfo ti = culture.TextInfo;

      Console.WriteLine("{0,-6}| {1,-15}| {2,-15}| {3,-15}",
                        culture.Name,
                        ti.ToUpper(text),
                        ti.ToLower(text),
                        ti.ToTitleCase(text));
    }
  }
}
実行結果
code  | ToUpper        | ToLower        | ToTitleCase    
------------------------------------------------------------
ja-JP | THE INTERNET   | the internet   | The Internet   
en-US | THE INTERNET   | the internet   | The Internet   
tr-TR | THE İNTERNET   | the internet   | The İnternet   
      | THE INTERNET   | the internet   | The Internet  

なお、StringクラスのToLower/ToUpper/ToUpperInvariant等のメソッドとの違いは文字列の加工・編集で詳しく解説しているのでご覧ください。

また、文字列の変換の他に、特定のカルチャで使用されるコードページの取得を行うことも出来ます。 次の例では、いくつかのカルチャでのTextInfoを取得し、TextInfoに設定されているコードページを表示しています。

using System;
using System.Globalization;
using System.Text;

class Sample {
  static void Main()
  {
    CultureInfo[] cultures = new CultureInfo[] {
      CultureInfo.GetCultureInfo("ja-JP"),
      CultureInfo.GetCultureInfo("en-US"),
      CultureInfo.GetCultureInfo("ru-RU"),
      CultureInfo.GetCultureInfo("zh-TW"),
      CultureInfo.GetCultureInfo("zh-CN"),
      CultureInfo.InvariantCulture,
    };

    Console.WriteLine("{0,-6}| {1,-8}| {2,-20}| {3,-8}| {4,-8}| {5,-8}",
                      "code",
                      "ANSI",
                      "Encoding.WebName",
                      "EBCDIC",
                      "OEM",
                      "Macintosh");

    Console.WriteLine(new string('-', 72));

    foreach (CultureInfo culture in cultures) {
      TextInfo ti = culture.TextInfo;

      Console.WriteLine("{0,-6}| {1,-8}| {2,-20}| {3,-8}| {4,-8}| {5,-8}",
                        culture.Name,
                        ti.ANSICodePage,
                        Encoding.GetEncoding(ti.ANSICodePage).WebName,
                        ti.EBCDICCodePage,
                        ti.OEMCodePage,
                        ti.MacCodePage);
    }
  }
}
実行結果
code  | ANSI    | Encoding.WebName    | EBCDIC  | OEM     | Macintosh
------------------------------------------------------------------------
ja-JP | 932     | shift_jis           | 20290   | 932     | 10001   
en-US | 1252    | Windows-1252        | 37      | 437     | 10000   
ru-RU | 1251    | windows-1251        | 20880   | 866     | 10007   
zh-TW | 950     | big5                | 500     | 950     | 10002   
zh-CN | 936     | gb2312              | 500     | 936     | 10008   
      | 1252    | Windows-1252        | 37      | 437     | 10000   

§3 カルチャと暦 (Calendar)

Calendarクラスおよびその派生クラスを使うことで、特定の暦を使った年数の計算・日時の変換を行うことが出来ます。 また、いくつかのCalendarの派生クラスはカルチャに設定することが出来、数値の時刻のフォーマットとは別に特定の暦に対応した表記に変更することも出来ます。

例えば、和暦を表すJapaneseCalendarクラスをカルチャに設定すると、年の表記に西暦ではなく年号が用いられるようになります。

using System;
using System.Globalization;
using System.Threading;

class Sample {
  static void Main()
  {
    // ja-JPのカルチャを作成
    CultureInfo jajp = new CultureInfo("ja-JP");

    DateTime dateTime = new DateTime(2010, 1, 23, 4, 5, 6);

    Console.WriteLine(dateTime.ToString("F", jajp));

    // 暦をJapaneseCalendarに変更
    jajp.DateTimeFormat.Calendar = new JapaneseCalendar();

    Console.WriteLine(dateTime.ToString("F", jajp));

    // スレッドのカルチャを、暦をJapaneseCalendarに変更したja-JPに変更
    Thread.CurrentThread.CurrentCulture = jajp;

    Console.WriteLine(dateTime.ToString("F"));
  }
}
実行結果
2010年1月23日 4:05:06
平成 22年1月23日 4:05:06
平成 22年1月23日 4:05:06

§3.1 日時の変換

DateTime型とCalendarクラスを組み合わせて使うことで、ある暦での日時を別の暦での日時に変換することが出来ます。 次の例は、JapaneseCalendarクラスを使って和暦と西暦(グレゴリオ暦)での日付を相互に変換するものです。

using System;
using System.Globalization;

class Sample {
  static void Main()
  {
    Calendar japaneseCalendar = new JapaneseCalendar();
    CultureInfo jajp = new CultureInfo("ja-JP");

    jajp.DateTimeFormat.Calendar = japaneseCalendar;

    DateTime dateTime;

    // 和暦で(現在の年号での)1年1月1日に相当する日のDateTimeを取得する
    dateTime = japaneseCalendar.ToDateTime(1, 1, 1, 0, 0, 0, 0, Calendar.CurrentEra);

    Console.WriteLine("{0} = {1}", dateTime.ToString("D", jajp), dateTime.ToString("D"));

    // 和暦で(現在の年号での)1年1月8日に相当する日のDateTimeを取得する
    dateTime = japaneseCalendar.ToDateTime(1, 1, 8, 0, 0, 0, 0, Calendar.CurrentEra);

    Console.WriteLine("{0} = {1}", dateTime.ToString("D", jajp), dateTime.ToString("D"));

    // 和暦で平成(=年号4)2年1月1日に相当する日のDateTimeを取得する
    dateTime = japaneseCalendar.ToDateTime(2, 1, 1, 0, 0, 0, 0, 4);

    Console.WriteLine("{0} = {1}", dateTime.ToString("D", jajp), dateTime.ToString("D"));

    // 和暦で昭和(=年号3)2年1月1日に相当する日のDateTimeを取得する
    dateTime = japaneseCalendar.ToDateTime(2, 1, 1, 0, 0, 0, 0, 3);

    Console.WriteLine("{0} = {1}", dateTime.ToString("D", jajp), dateTime.ToString("D"));

    Console.WriteLine();

    // 西暦での日付に対応する和暦での年号と年を取得する
    string[] eraNames = new string[] {"(改元前)", "明治", "大正", "昭和", "平成"};

    foreach (DateTime dt in new DateTime[] {
      new DateTime(2000, 1, 1),
      new DateTime(1975, 1, 1),
      new DateTime(1950, 1, 1),
      new DateTime(1925, 1, 1),
      new DateTime(1900, 1, 1),
      new DateTime(1868, 9, 8),
      new DateTime(1868, 9, 7),
    })
    {
      Console.WriteLine("{0} => {1} {2}年",
                        dt.ToString("D"),
                        eraNames[japaneseCalendar.GetEra(dt)],
                        japaneseCalendar.GetYear(dt));
    }
  }
}
実行結果
昭和 64年1月1日 = 1989年1月1日
平成 1年1月8日 = 1989年1月8日
平成 2年1月1日 = 1990年1月1日
昭和 2年1月1日 = 1927年1月1日

2000年1月1日 => 平成 12年
1975年1月1日 => 昭和 50年
1950年1月1日 => 昭和 25年
1925年1月1日 => 大正 14年
1900年1月1日 => 明治 33年
1868年9月8日 => 明治 1年

ハンドルされていない例外: System.ArgumentOutOfRangeException: 指定された時刻はこのカレンダーではサポートされていません。値は 09/08/1868 00:00:00 (グレゴリオ暦)から 12/31/9999 23:59:59 (グレゴリオ暦) までの間でなければなりません。
パラメータ名: time
   場所 System.Globalization.GregorianCalendarHelper.CheckTicksRange(Int64 ticks)
   場所 System.Globalization.GregorianCalendarHelper.GetDatePart(Int64 ticks, Int32 part)
   場所 System.Globalization.GregorianCalendarHelper.GetYear(DateTime time)
   場所 System.Globalization.JapaneseCalendar.GetYear(DateTime time)
   場所 Sample.Main()

スローされている例外のメッセージにもあるとおり、JapaneseCalendarクラスで実装される暦ではグレゴリオ暦1868年9月8日より前の日付は定義されていません。

§3.2 日時の加減算

DateTime型ではグレゴリオ暦が用いられるため、DateTime.Add・DateTime.Subtractメソッドでは1年が12ヶ月365日、1週が7日、グレゴリオ暦での閏年の定義に基づいて日時の加減算が行われます。 Calendarクラスを用いることで、特定の暦での年・月・週および置閏の規則に基づいた日時の加減算を行うことが出来ます。 次の例では、.NET Frameworkで実装されている暦を使って基準となる日から数年・数ヶ月・数週前後の日付を求めています。

using System;
using System.Globalization;
using System.Threading;

class Sample {
  static void Main()
  {
    Calendar[] calendars = new Calendar[] {
      new GregorianCalendar(),
      new JapaneseCalendar(),
      new JapaneseLunisolarCalendar(),
      new JulianCalendar(),
      new HebrewCalendar(),
      new HijriCalendar(),
    };

    // 書式指定文字"D"の書式を"yyyy-MM-dd"に変更
    Thread.CurrentThread.CurrentCulture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
    Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongDatePattern = "yyyy-MM-dd";

    // 基準となる日付
    DateTime baseDateTime = new DateTime(2001, 1, 1);

    Console.WriteLine("{0,-25}|{1,-12}|{2,-12}|{3,-12}|{4,-12}|{5,-12}|{6,-12}|{7,-12}|{8,-12}|{9,-12}|{10,-12}|{11,-12}|{12,-12}|{13,-12}|{14,-12}|{15,-12}|",
                      "Calendar",
                      "-10 years", "-1 year", "-12months", "-3 months", "-1 month", "-4 weeks", "-1 week",
                      "0",
                      "+1 week", "+4 week", "+1 month", "+3 months", "+12 months", "+1 year", "+10 years");

    Console.WriteLine(new string('-', 220));

    foreach (Calendar calendar in calendars) {
      Console.WriteLine("{0,-25}|{1,-12:D}|{2,-12:D}|{3,-12:D}|{4,-12:D}|{5,-12:D}|{6,-12:D}|{7,-12:D}|{8,-12:D}|{9,-12:D}|{10,-12:D}|{11,-12:D}|{12,-12:D}|{13,-12:D}|{14,-12:D}|{15,-12:D}|",
                        calendar.GetType().Name,
                        calendar.AddYears(baseDateTime, -10),   // 10年前
                        calendar.AddYears(baseDateTime, -1),    // 1年前
                        calendar.AddMonths(baseDateTime, -12),  // 12ヶ月前
                        calendar.AddMonths(baseDateTime, -3),   // 3ヶ月前
                        calendar.AddMonths(baseDateTime, -1),   // 1ヶ月前
                        calendar.AddWeeks(baseDateTime, -4),    // 4週間前
                        calendar.AddWeeks(baseDateTime, -1),    // 1週間前
                        baseDateTime,
                        calendar.AddWeeks(baseDateTime, +1),    // 1週間後
                        calendar.AddWeeks(baseDateTime, +4),    // 4週間後
                        calendar.AddMonths(baseDateTime, +1),   // 1ヶ月後
                        calendar.AddMonths(baseDateTime, +3),   // 3ヶ月後
                        calendar.AddMonths(baseDateTime, +12),  // 12ヶ月後
                        calendar.AddYears(baseDateTime, +1),    // 1年後
                        calendar.AddYears(baseDateTime, +10));  // 10年後
    }
  }
}
実行結果
Calendar                 |-10 years   |-1 year     |-12months   |-3 months   |-1 month    |-4 weeks    |-1 week     |0           |+1 week     |+4 week     |+1 month    |+3 months   |+12 months  |+1 year     |+10 years   |
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GregorianCalendar        |1991-01-01  |2000-01-01  |2000-01-01  |2000-10-01  |2000-12-01  |2000-12-04  |2000-12-25  |2001-01-01  |2001-01-08  |2001-01-29  |2001-02-01  |2001-04-01  |2002-01-01  |2002-01-01  |2011-01-01  |
JapaneseCalendar         |1991-01-01  |2000-01-01  |2000-01-01  |2000-10-01  |2000-12-01  |2000-12-04  |2000-12-25  |2001-01-01  |2001-01-08  |2001-01-29  |2001-02-01  |2001-04-01  |2002-01-01  |2002-01-01  |2011-01-01  |
JapaneseLunisolarCalendar|1990-12-23  |2000-01-13  |2000-01-13  |2000-10-04  |2000-12-02  |2000-12-04  |2000-12-25  |2001-01-01  |2001-01-08  |2001-01-29  |2001-01-30  |2001-03-31  |2001-12-21  |2001-12-21  |2011-01-10  |
JulianCalendar           |1991-01-01  |2000-01-01  |2000-01-01  |2000-10-02  |2000-12-02  |2000-12-04  |2000-12-25  |2001-01-01  |2001-01-08  |2001-01-29  |2001-02-01  |2001-04-01  |2002-01-01  |2002-01-01  |2011-01-01  |
HebrewCalendar           |1990-12-23  |1999-12-15  |2000-01-13  |2000-10-05  |2000-12-03  |2000-12-04  |2000-12-25  |2001-01-01  |2001-01-08  |2001-01-29  |2001-01-30  |2001-03-30  |2001-12-21  |2001-12-21  |2010-12-13  |
HijriCalendar            |1991-04-20  |2000-01-12  |2000-01-12  |2000-10-04  |2000-12-02  |2000-12-04  |2000-12-25  |2001-01-01  |2001-01-08  |2001-01-29  |2001-01-30  |2001-03-30  |2001-12-21  |2001-12-21  |2010-09-14  |

結果からも分かるとおり、ユリウス暦(JulianCalendar)、ヘブライ暦(HebrewCalendar)やイスラム歴(HijriCalendar)では一年・一月の数え方がグレゴリオ暦(GregorianCalendar)とは異なるので、グレゴリオ暦での一年後・一月後とは異なる日付となります。

和暦(JapaneseCalendar)では年号が異なるだけでそれ以外はグレゴリオ暦と同じなので、加減算の結果は同じとなります。 なお、JapaneseLunisolarCalendarは太陽年および太陰月に基づく太陰太陽暦を実装するクラスです。