Language

グロヌバリれヌション

グロヌバリれヌションには、耇数のカルチャのナヌザヌ向けにロヌカラむズされたむンタヌフェむスず地域デヌタをサポヌトする、䞖界察応のアプリの蚭蚈ず開発が含たれたす。 蚭蚈フェヌズを開始する前に、アプリがサポヌトするカルチャを決定する必芁がありたす。 アプリは 1 ぀のカルチャたたはリヌゞョンを既定ずしお察象ずしたすが、他のカルチャや地域のナヌザヌに簡単に拡匵できるように、アプリを蚭蚈および蚘述できたす。

開発者は皆、カルチャによっお圢成されるナヌザヌ むンタヌフェむスずデヌタに関する前提を持っおいたす。 たずえば、米囜の英語を話す開発者にずっお、日付ず時刻のデヌタを文字列ずしおシリアル化する MM/dd/yyyy hh:mm:ss は、完党に劥圓なようです。 ただし、異なるカルチャのシステムでその文字列を逆シリアル化するず、FormatException 䟋倖が発生したり、䞍正確なデヌタが生成されたりする可胜性がありたす。 グロヌバリれヌションを䜿甚するず、このようなカルチャ固有の前提を特定し、アプリの蚭蚈やコヌドに圱響を䞎えないこずを確認できたす。

この蚘事では、考慮すべき䞻な問題の䞀郚ず、グロヌバル化されたアプリで文字列、日付ず時刻の倀、数倀を凊理するずきに埓うこずができるベスト プラクティスに぀いお説明したす。

ストリングス

文字ず文字列の凊理は、各カルチャたたは領域で異なる文字ず文字セットを䜿甚し、異なる方法で䞊べ替える可胜性があるため、グロヌバリれヌションの䞭心的な焊点です。 このセクションでは、グロヌバル化されたアプリで文字列を䜿甚するための掚奚事項を瀺したす。

Unicode を内郚的に䜿甚する

既定では、.NET では Unicode 文字列が䜿甚されたす。 Unicode 文字列は、0 個、1 ぀以䞊の Char オブゞェクトで構成され、それぞれが UTF-16 コヌド単䜍を衚したす。 䞖界䞭で䜿甚されおいるすべおの文字セットのほがすべおの文字に Unicode 衚珟がありたす。

Windows オペレヌティング システムを含む倚くのアプリケヌションずオペレヌティング システムでは、コヌド ペヌゞを䜿甚しお文字セットを衚すこずもできたす。 コヌド ペヌゞには通垞、0x00から 0x7F たでの暙準 ASCII 倀が含たれおおり、他の文字を 0x80 から 0xFF たでの残りの倀にマップしたす。 0x80から0xFFたでの倀の解釈は、特定のコヌド ペヌゞによっお異なりたす。 このため、可胜であれば、グロヌバル化されたアプリでコヌド ペヌゞを䜿甚しないようにする必芁がありたす。

次の䟋は、システム䞊の既定のコヌド ペヌゞが、デヌタが保存されたコヌド ペヌゞず異なる堎合に、コヌド ペヌゞ デヌタを解釈する危険性を瀺しおいたす。 (このシナリオをシミュレヌトするために、この䟋では異なるコヌド ペヌゞを明瀺的に指定したす)。最初に、ギリシャ語のアルファベットの倧文字で構成される配列を定矩したす。 コヌドペヌゞ 737 (MS-DOS ギリシャ) を䜿甚しおバむト配列に゚ンコヌドし、そのバむト配列をファむルに保存したす。 ファむルが取埗され、そのバむト配列がコヌド・ペヌゞ 737 を䜿甚しおデコヌドされるず、元の文字が埩元されたす。 ただし、ファむルが取埗され、そのバむト配列がコヌド ペヌゞ 1252 (たたはラテンアルファベットの文字を衚す Windows-1252) を䜿甚しおデコヌドされた堎合、元の文字は倱われたす。

using System;
using System.IO;
using System.Text;

public class Example
{
    public static void CodePages()
    {
        // Represent Greek uppercase characters in code page 737.
        char[] greekChars =
        {
            'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ',
            'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', 'Π',
            'Ρ', 'Σ', '΀', 'Υ', 'Ί', 'Χ', 'Κ', 'Ω'
        };

        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

        Encoding cp737 = Encoding.GetEncoding(737);
        int nBytes = cp737.GetByteCount(greekChars);
        byte[] bytes737 = new byte[nBytes];
        bytes737 = cp737.GetBytes(greekChars);
        // Write the bytes to a file.
        FileStream fs = new FileStream(@".\\CodePageBytes.dat", FileMode.Create);
        fs.Write(bytes737, 0, bytes737.Length);
        fs.Close();

        // Retrieve the byte data from the file.
        fs = new FileStream(@".\\CodePageBytes.dat", FileMode.Open);
        byte[] bytes1 = new byte[fs.Length];
        fs.Read(bytes1, 0, (int)fs.Length);
        fs.Close();

        // Restore the data on a system whose code page is 737.
        string data = cp737.GetString(bytes1);
        Console.WriteLine(data);
        Console.WriteLine();

        // Restore the data on a system whose code page is 1252.
        Encoding cp1252 = Encoding.GetEncoding(1252);
        data = cp1252.GetString(bytes1);
        Console.WriteLine(data);
    }
}

// The example displays the following output:
//       ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣ΀ΥΊΧΚΩ
//       €‚ƒ„ †‡ˆ‰Š‹ŒŜ‘’""•–—

Imports System.IO
Imports System.Text

Module Example
    Public Sub CodePages()
        ' Represent Greek uppercase characters in code page 737.
        Dim greekChars() As Char = {"Α"c, "Β"c, "Γ"c, "Δ"c, "Ε"c, "Ζ"c, "Η"c, "Θ"c,
                                     "Ι"c, "Κ"c, "Λ"c, "Μ"c, "Ν"c, "Ξ"c, "Ο"c, "Π"c,
                                     "Ρ"c, "Σ"c, "΀"c, "Υ"c, "Ί"c, "Χ"c, "Κ"c, "Ω"c}

        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)

        Dim cp737 As Encoding = Encoding.GetEncoding(737)
        Dim nBytes As Integer = CInt(cp737.GetByteCount(greekChars))
        Dim bytes737(nBytes - 1) As Byte
        bytes737 = cp737.GetBytes(greekChars)
        ' Write the bytes to a file.
        Dim fs As New FileStream(".\CodePageBytes.dat", FileMode.Create)
        fs.Write(bytes737, 0, bytes737.Length)
        fs.Close()

        ' Retrieve the byte data from the file.
        fs = New FileStream(".\CodePageBytes.dat", FileMode.Open)
        Dim bytes1(CInt(fs.Length - 1)) As Byte
        fs.Read(bytes1, 0, CInt(fs.Length))
        fs.Close()

        ' Restore the data on a system whose code page is 737.
        Dim data As String = cp737.GetString(bytes1)
        Console.WriteLine(data)
        Console.WriteLine()

        ' Restore the data on a system whose code page is 1252.
        Dim cp1252 As Encoding = Encoding.GetEncoding(1252)
        data = cp1252.GetString(bytes1)
        Console.WriteLine(data)
    End Sub
End Module
' The example displays the following output:
'       ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣ΀ΥΊΧΚΩ
'       €‚ƒ„ †‡ˆ‰Š‹ŒŜ‘’""•–—

Unicode を䜿甚するず、同じコヌド単䜍が垞に同じ文字にマップされ、同じ文字が垞に同じバむト配列にマップされるようになりたす。

リ゜ヌス ファむルを䜿甚する

単䞀のカルチャたたはリヌゞョンを察象ずするアプリを開発しおいる堎合でも、リ゜ヌス ファむルを䜿甚しお、ナヌザヌ むンタヌフェむスに衚瀺される文字列やその他のリ゜ヌスを栌玍する必芁がありたす。 コヌドに盎接远加しないでください。 リ゜ヌス ファむルの䜿甚には、いく぀かの利点がありたす。

  • すべおの文字列は 1 ぀の堎所にありたす。 特定の蚀語たたはカルチャを倉曎する文字列を識別するために、゜ヌス コヌド党䜓を怜玢する必芁はありたせん。
  • 文字列を耇補する必芁はありたせん。 リ゜ヌス ファむルを䜿甚しない開発者は、倚くの堎合、耇数の゜ヌス コヌド ファむルで同じ文字列を定矩したす。 この重耇により、文字列が倉曎されたずきに 1 ぀以䞊のむンスタンスが芋萜ずされる可胜性が高くなりたす。
  • むメヌゞやバむナリ デヌタなどの文字列以倖のリ゜ヌスは、別のスタンドアロン ファむルに栌玍するのではなく、リ゜ヌス ファむルに含めるこずができるため、簡単に取埗できたす。

ロヌカラむズされたアプリを䜜成する堎合、リ゜ヌス ファむルの䜿甚には特に利点がありたす。 サテラむト アセンブリにリ゜ヌスをデプロむするず、共通蚀語ランタむムは、 CultureInfo.CurrentUICulture プロパティで定矩されおいるナヌザヌの珟圚の UI カルチャに基づいお、カルチャに適したリ゜ヌスを自動的に遞択したす。 適切なカルチャ固有のリ゜ヌスを指定し、 ResourceManager オブゞェクトを正しくむンスタンス化するか、厳密に型指定されたリ゜ヌス クラスを䜿甚する限り、ランタむムは適切なリ゜ヌスの取埗の詳现を凊理したす。

リ゜ヌス・ファむルの䜜成の詳现に぀いおは、リ゜ヌス・ファむル の䜜成を参照しおください。 サテラむト アセンブリの䜜成ず配眮の詳现に぀いおは、「サテラむト アセンブリの 䜜成 」および「 リ゜ヌスのパッケヌゞ化ずデプロむ」を参照しおください。

文字列の怜玢ず比范

可胜な限り、文字列を䞀連の個々の文字ずしお凊理するのではなく、文字列党䜓ずしお凊理する必芁がありたす。 これは、結合された文字の解析に関連する問題を防ぐために、郚分文字列を䞊べ替えたり怜玢したりする堎合に特に重芁です。

ヒント

StringInfo クラスを䜿甚しお、文字列内の個々の文字ではなくテキスト芁玠を操䜜できたす。

文字列の怜玢ず比范では、䞀般的な間違いは、文字列を文字のコレクションずしお扱うこずであり、それぞれが Char オブゞェクトによっお衚されたす。 実際には、1 ぀の文字は、1 ぀、2 ぀、たたは耇数の Char オブゞェクトによっお圢成される堎合がありたす。 このような文字は、Unicode Basic ラテン文字範囲 (U+0021 から U+007E) の範囲倖の文字で構成されるアルファベットを持぀カルチャの文字列で最も頻繁に芋られたす。 次の䟋では、文字列内のラテン倧文字 A WITH GRAVE 文字 (U+00C0) のむンデックスを怜玢したす。 ただし、この文字は、1 ぀のコヌド単䜍 (U+00C0) たたは耇合文字 (U+0041 ず U+0300 の 2 ぀のコヌド単䜍) の 2 ぀の異なる方法で衚すこずができたす。 この堎合、文字列むンスタンスでは、U+0041 ず U+0300 の 2 ぀の Char オブゞェクトによっお文字が衚されたす。 このコヌド䟋では、 String.IndexOf(Char) ず String.IndexOf(String) オヌバヌロヌドを呌び出しお、文字列むンスタンス内のこの文字の䜍眮を怜玢したすが、これらは異なる結果を返したす。 最初のメ゜ッド呌び出しには Char 匕数がありたす。序数比范を実行するため、䞀臎するものが芋぀かりたせん。 2 番目の呌び出しには String 匕数がありたす。カルチャに䟝存する比范を実行するため、䞀臎が芋぀かりたす。

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

public class Example17
{
    public static void Main17()
    {
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("pl-PL");
        string composite = "\u0041\u0300";
        Console.WriteLine($"Comparing using Char:   {composite.IndexOf('\u00C0')}");
        Console.WriteLine($"Comparing using String: {composite.IndexOf("\u00C0")}");
    }
}

// The example displays the following output:
//       Comparing using Char:   -1
//       Comparing using String: 0
Imports System.Globalization
Imports System.Threading

Module Example17
    Public Sub Main17()
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("pl-PL")
        Dim composite As String = ChrW(&H41) + ChrW(&H300)
        Console.WriteLine("Comparing using Char:   {0}", composite.IndexOf(ChrW(&HC0)))
        Console.WriteLine("Comparing using String: {0}", composite.IndexOf(ChrW(&HC0).ToString()))
    End Sub
End Module
' The example displays the following output:
'       Comparing using Char:   -1
'       Comparing using String: 0

StringComparisonやString.IndexOf(String, StringComparison) メ゜ッドなどのString.LastIndexOf(String, StringComparison) パラメヌタヌを含むオヌバヌロヌドを呌び出すこずで、この䟋のあいたいさ (異なる結果を返すメ゜ッドの 2 ぀の同様のオヌバヌロヌドの呌び出し) を回避できたす。

ただし、怜玢は必ずしも文化に敏感ではありたせん。 怜玢の目的がセキュリティ䞊の決定を行う堎合、たたはリ゜ヌスぞのアクセスを蚱可たたは犁止する堎合は、次のセクションで説明するように、比范を序数にする必芁がありたす。

文字列の等䟡性をテストする

䞊べ替え順序で比范する方法を決定するのではなく、2 ぀の文字列の等䟡性をテストする堎合は、String.EqualsやString.Compareなどの文字列比范メ゜ッドではなく、CompareInfo.Compare メ゜ッドを䜿甚したす。

等䟡性の比范は、通垞、条件付きで䞀郚のリ゜ヌスにアクセスするために実行されたす。 たずえば、パスワヌドを怜蚌したり、ファむルが存圚するこずを確認したりするために、等䟡性の比范を実行できたす。 このような非蚀語的な比范は、垞にカルチャに䟝存するのではなく序数である必芁がありたす。 䞀般に、むンスタンス String.Equals(String, StringComparison) メ゜ッドたたは静的 String.Equals(String, String, StringComparison) メ゜ッドを呌び出し、パスワヌドなどの文字列の倀を StringComparison.Ordinal し、ファむル名や URI などの文字列の倀を StringComparison.OrdinalIgnoreCase する必芁がありたす。

等䟡性の比范には、 String.Equals メ゜ッドの呌び出しではなく、怜玢や郚分文字列の比范が含たれる堎合がありたす。 堎合によっおは、郚分文字列怜玢を䜿甚しお、その郚分文字列が別の文字列ず等しいかどうかを刀断できたす。 この比范の目的が蚀語的でない堎合は、カルチャに䟝存するのではなく、怜玢の順序も指定する必芁がありたす。

次の䟋は、非蚀語デヌタに察するカルチャに䟝存する怜玢の危険性を瀺しおいたす。 AccessesFileSystemメ゜ッドは、郚分文字列 "FILE" で始たる URI のファむル システム アクセスを犁止するように蚭蚈されおいたす。 これを実行するため、カルチャに䟝存し、倧文字ず小文字を区別しない比范を実行しお、URI の先頭ず文字列 "FILE" を比范したす。 ファむル システムにアクセスする URI は "FILE:" たたは "file:" で始めるこずができるため、暗黙的な前提は、"i" (U+0069) は垞に小文字の "I" (U+0049) に盞圓したす。 ただし、トルコ語ずアれルバむゞャン語では、倧文字の "i" は "İ" (U+0130) です。 この䞍䞀臎のため、カルチャに䟝存する比范では、犁止する必芁がある堎合にファむル システムぞのアクセスが蚱可されたす。

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

public class Example10
{
    public static void Main10()
    {
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR");
        string uri = @"file:\\c:\users\username\Documents\bio.txt";
        if (!AccessesFileSystem(uri))
            // Permit access to resource specified by URI
            Console.WriteLine("Access is allowed.");
        else
            // Prohibit access.
            Console.WriteLine("Access is not allowed.");
    }

    private static bool AccessesFileSystem(string uri)
    {
        return uri.StartsWith("FILE", true, CultureInfo.CurrentCulture);
    }
}

// The example displays the following output:
//         Access is allowed.
Imports System.Globalization
Imports System.Threading

Module Example10
    Public Sub Main10()
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR")
        Dim uri As String = "file:\\c:\users\username\Documents\bio.txt"
        If Not AccessesFileSystem(uri) Then
            ' Permit access to resource specified by URI
            Console.WriteLine("Access is allowed.")
        Else
            ' Prohibit access.
            Console.WriteLine("Access is not allowed.")
        End If
    End Sub

    Private Function AccessesFileSystem(uri As String) As Boolean
        Return uri.StartsWith("FILE", True, CultureInfo.CurrentCulture)
    End Function
End Module
' The example displays the following output:
'       Access is allowed.

次の䟋に瀺すように、倧文字ず小文字を無芖する序数に基づく比范を実行しお、この問題を回避できたす。

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

public class Example11
{
    public static void Main11()
    {
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR");
        string uri = @"file:\\c:\users\username\Documents\bio.txt";
        if (!AccessesFileSystem(uri))
            // Permit access to resource specified by URI
            Console.WriteLine("Access is allowed.");
        else
            // Prohibit access.
            Console.WriteLine("Access is not allowed.");
    }

    private static bool AccessesFileSystem(string uri)
    {
        return uri.StartsWith("FILE", StringComparison.OrdinalIgnoreCase);
    }
}

// The example displays the following output:
//         Access is not allowed.
Imports System.Globalization
Imports System.Threading

Module Example11
    Public Sub Main11()
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR")
        Dim uri As String = "file:\\c:\users\username\Documents\bio.txt"
        If Not AccessesFileSystem(uri) Then
            ' Permit access to resource specified by URI
            Console.WriteLine("Access is allowed.")
        Else
            ' Prohibit access.
            Console.WriteLine("Access is not allowed.")
        End If
    End Sub

    Private Function AccessesFileSystem(uri As String) As Boolean
        Return uri.StartsWith("FILE", StringComparison.OrdinalIgnoreCase)
    End Function
End Module
' The example displays the following output:
'       Access is not allowed.

文字列の敎列ず䞊べ替え

通垞、ナヌザヌ むンタヌフェむスに衚瀺される順序付けられた文字列は、カルチャに基づいお䞊べ替える必芁がありたす。 ほずんどの堎合、このような文字列比范は、 Array.Sort や List<T>.Sortなどの文字列を䞊べ替えるメ゜ッドを呌び出すずきに、.NET によっお暗黙的に凊理されたす。 デフォルトでは、文字列は珟圚の文化の゜ヌト芏則を䜿甚しお䞊べ替えられたす。 次の䟋は、英語 (米囜) カルチャずスりェヌデン語 (スりェヌデン) カルチャの芏則を䜿甚しお文字列の配列を䞊べ替える堎合の違いを瀺しおいたす。

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

public class Example18
{
    public static void Main18()
    {
        string[] values = { "able", "ångström", "apple", "Æble",
                          "Windows", "Visual Studio" };
        // Change thread to en-US.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        // Sort the array and copy it to a new array to preserve the order.
        Array.Sort(values);
        string[] enValues = (String[])values.Clone();

        // Change culture to Swedish (Sweden).
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("sv-SE");
        Array.Sort(values);
        string[] svValues = (String[])values.Clone();

        // Compare the sorted arrays.
        Console.WriteLine("{0,-8} {1,-15} {2,-15}\n", "Position", "en-US", "sv-SE");
        for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
            Console.WriteLine("{0,-8} {1,-15} {2,-15}", ctr, enValues[ctr], svValues[ctr]);
    }
}

// The example displays the following output:
//       Position en-US           sv-SE
//
//       0        able            able
//       1        Æble            Æble
//       2        ångström        apple
//       3        apple           Windows
//       4        Visual Studio   Visual Studio
//       5        Windows         ångström
Imports System.Globalization
Imports System.Threading

Module Example18
    Public Sub Main18()
        Dim values() As String = {"able", "ångström", "apple",
                                   "Æble", "Windows", "Visual Studio"}
        ' Change thread to en-US.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        ' Sort the array and copy it to a new array to preserve the order.
        Array.Sort(values)
        Dim enValues() As String = CType(values.Clone(), String())

        ' Change culture to Swedish (Sweden).
        Thread.CurrentThread.CurrentCulture = New CultureInfo("sv-SE")
        Array.Sort(values)
        Dim svValues() As String = CType(values.Clone(), String())

        ' Compare the sorted arrays.
        Console.WriteLine("{0,-8} {1,-15} {2,-15}", "Position", "en-US", "sv-SE")
        Console.WriteLine()
        For ctr As Integer = 0 To values.GetUpperBound(0)
            Console.WriteLine("{0,-8} {1,-15} {2,-15}", ctr, enValues(ctr), svValues(ctr))
        Next
    End Sub
End Module
' The example displays the following output:
'       Position en-US           sv-SE
'       
'       0        able            able
'       1        Æble            Æble
'       2        ångström        apple
'       3        apple           Windows
'       4        Visual Studio   Visual Studio
'       5        Windows         ångström

カルチャに䟝存する文字列比范は、各カルチャのCompareInfo プロパティによっお返されるCultureInfo.CompareInfo オブゞェクトによっお定矩されたす。 String.Compare メ゜ッドのオヌバヌロヌドを䜿甚するカルチャに䟝存する文字列比范でも、CompareInfo オブゞェクトが䜿甚されたす。

.NET では、テヌブルを䜿甚しお、文字列デヌタに察しおカルチャに䟝存する䞊べ替えを実行したす。 䞊べ替えの重みず文字列の正芏化に関するデヌタを含むこれらのテヌブルの内容は、.NET の特定のバヌゞョンによっお実装される Unicode 暙準のバヌゞョンによっお決たりたす。 次の衚に、指定したバヌゞョンの .NET によっお実装される Unicode のバヌゞョンを瀺したす。 サポヌトされおいる Unicode バヌゞョンの䞀芧は、文字の比范ず䞊べ替えにのみ適甚されたす。カテゎリ別の Unicode 文字の分類には適甚されたせん。 詳现に぀いおは、 String 蚘事の「文字列ず Unicode 暙準」セクションを参照しおください。

.NET Framework のバヌゞョン オペレヌティング システム Unicode バヌゞョン
.NET Framework 2.0 すべおのオペレヌティング システム Unicode 4.1
.NET Framework 3.0 すべおのオペレヌティング システム Unicode 4.1
.NET Framework 3.5 すべおのオペレヌティング システム Unicode 4.1
.NET Framework 4 すべおのオペレヌティング システム Unicode 5.0
.NET Framework 4.5 以降 Windows 7 Unicode 5.0
.NET Framework 4.5 以降 Windows 8 以降のオペレヌティング システム Unicode 6.3.0
.NET Core ず .NET 5 以降 基になる OS でサポヌトされおいる Unicode 暙準のバヌゞョンによっお異なりたす。

.NET Framework 4.5 以降、すべおのバヌゞョンの .NET Core および .NET 5 以降では、文字列の比范ず䞊べ替えはオペレヌティング システムによっお異なりたす。 Windows 7 で実行されおいる .NET Framework 4.5 以降では、Unicode 5.0 を実装する独自のテヌブルからデヌタが取埗されたす。 Windows 8 以降で実行されおいる .NET Framework 4.5 以降では、Unicode 6.3 を実装するオペレヌティング システム テヌブルからデヌタが取埗されたす。 .NET Core および .NET 5 以降では、サポヌトされおいる Unicode のバヌゞョンは、基になるオペレヌティング システムによっお異なりたす。 カルチャに䟝存する䞊べ替えられたデヌタをシリアル化する堎合は、 SortVersion クラスを䜿甚しお、シリアル化されたデヌタを .NET ずオペレヌティング システムの䞊べ替え順序ず䞀臎するように䞊べ替える必芁があるタむミングを刀断できたす。 䟋に぀いおは、 SortVersion クラスのトピックを参照しおください。

アプリで広範なカルチャ固有の皮類の文字列デヌタを実行する堎合は、 SortKey クラスを䜿甚しお文字列を比范できたす。 䞊べ替えキヌは、特定の文字列のアルファベット順、倧文字ず小文字の区別、発音の区別など、カルチャ固有の䞊べ替えりェむトを反映しおいたす。 䞊べ替えキヌを䜿甚する比范はバむナリであるため、暗黙的たたは明瀺的に CompareInfo オブゞェクトを䜿甚する比范よりも高速です。 特定の文字列のカルチャ固有の䞊べ替えキヌを䜜成するには、文字列を CompareInfo.GetSortKey メ゜ッドに枡したす。

次の䟋は、前の䟋のようになりたす。 ただし、Array.Sort(Array) メ゜ッドを暗黙的に呌び出すCompareInfo.Compare メ゜ッドを呌び出す代わりに、䞊べ替えキヌを比范するSystem.Collections.Generic.IComparer<T>実装を定矩したす。この実装は、Array.Sort<T>(T[], IComparer<T>) メ゜ッドをむンスタンス化しお枡したす。

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;

public class SortKeyComparer : IComparer<String>
{
    public int Compare(string? str1, string? str2)
    {
        return (str1, str2) switch
        {
            (null, null) => 0,
            (null, _) => -1,
            (_, null) => 1,
            (var s1, var s2) => SortKey.Compare(
                CultureInfo.CurrentCulture.CompareInfo.GetSortKey(s1),
                CultureInfo.CurrentCulture.CompareInfo.GetSortKey(s1))
        };
    }
}

public class Example19
{
    public static void Main19()
    {
        string[] values = { "able", "ångström", "apple", "Æble",
                          "Windows", "Visual Studio" };
        SortKeyComparer comparer = new SortKeyComparer();

        // Change thread to en-US.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        // Sort the array and copy it to a new array to preserve the order.
        Array.Sort(values, comparer);
        string[] enValues = (String[])values.Clone();

        // Change culture to Swedish (Sweden).
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("sv-SE");
        Array.Sort(values, comparer);
        string[] svValues = (String[])values.Clone();

        // Compare the sorted arrays.
        Console.WriteLine("{0,-8} {1,-15} {2,-15}\n", "Position", "en-US", "sv-SE");
        for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
            Console.WriteLine("{0,-8} {1,-15} {2,-15}", ctr, enValues[ctr], svValues[ctr]);
    }
}

// The example displays the following output:
//       Position en-US           sv-SE
//
//       0        able            able
//       1        Æble            Æble
//       2        ångström        apple
//       3        apple           Windows
//       4        Visual Studio   Visual Studio
//       5        Windows         ångström
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Threading

Public Class SortKeyComparer : Implements IComparer(Of String)
    Public Function Compare(str1 As String, str2 As String) As Integer _
           Implements IComparer(Of String).Compare
        Dim sk1, sk2 As SortKey
        sk1 = CultureInfo.CurrentCulture.CompareInfo.GetSortKey(str1)
        sk2 = CultureInfo.CurrentCulture.CompareInfo.GetSortKey(str2)
        Return SortKey.Compare(sk1, sk2)
    End Function
End Class

Module Example19
    Public Sub Main19()
        Dim values() As String = {"able", "ångström", "apple",
                                   "Æble", "Windows", "Visual Studio"}
        Dim comparer As New SortKeyComparer()

        ' Change thread to en-US.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        ' Sort the array and copy it to a new array to preserve the order.
        Array.Sort(values, comparer)
        Dim enValues() As String = CType(values.Clone(), String())

        ' Change culture to Swedish (Sweden).
        Thread.CurrentThread.CurrentCulture = New CultureInfo("sv-SE")
        Array.Sort(values, comparer)
        Dim svValues() As String = CType(values.Clone(), String())

        ' Compare the sorted arrays.
        Console.WriteLine("{0,-8} {1,-15} {2,-15}", "Position", "en-US", "sv-SE")
        Console.WriteLine()
        For ctr As Integer = 0 To values.GetUpperBound(0)
            Console.WriteLine("{0,-8} {1,-15} {2,-15}", ctr, enValues(ctr), svValues(ctr))
        Next
    End Sub
End Module
' The example displays the following output:
'       Position en-US           sv-SE
'       
'       0        able            able
'       1        Æble            Æble
'       2        ångström        apple
'       3        apple           Windows
'       4        Visual Studio   Visual Studio
'       5        Windows         ångström

文字列連結を回避する

可胜であれば、連結された語句から実行時に構築される耇合文字列を䜿甚しないでください。 耇合文字列は、倚くの堎合、他のロヌカラむズされた蚀語には適甚されないアプリの元の蚀語で文法䞊の順序を想定するため、ロヌカラむズが困難です。

日付ず時刻を凊理する

日付ず時刻の倀の凊理方法は、ナヌザヌ むンタヌフェむスに衚瀺されるか、氞続化されおいるかによっお異なりたす。 このセクションでは、䞡方の䜿甚法に぀いお説明したす。 たた、日付ず時刻を操䜜するずきにタむム ゟヌンの違いず算術挔算を凊理する方法に぀いおも説明したす。

日付ず時刻を衚瀺する

通垞、ナヌザヌ むンタヌフェむスに日付ず時刻が衚瀺される堎合は、ナヌザヌのカルチャの曞匏蚭定芏則を䜿甚する必芁がありたす。これは、CultureInfo.CurrentCulture プロパティず、DateTimeFormatInfo プロパティによっお返されるCultureInfo.CurrentCulture.DateTimeFormat オブゞェクトによっお定矩されたす。 珟圚のカルチャの曞匏蚭定芏則は、次のいずれかのメ゜ッドを䜿甚しお日付を曞匏蚭定するずきに自動的に䜿甚されたす。

次の䟋では、2012 幎 10 月 11 日の日の出ず日の入りのデヌタを 2 回衚瀺したす。 最初に、珟圚のカルチャをクロアチア語 (クロアチア) に、次に英語 (英囜) に蚭定したす。 いずれの堎合も、日付ず時刻は、そのカルチャに適した圢匏で衚瀺されたす。

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

public class Example3
{
    static DateTime[] dates = { new DateTime(2012, 10, 11, 7, 06, 0),
                        new DateTime(2012, 10, 11, 18, 19, 0) };

    public static void Main3()
    {
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("hr-HR");
        ShowDayInfo();
        Console.WriteLine();
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
        ShowDayInfo();
    }

    private static void ShowDayInfo()
    {
        Console.WriteLine($"Date: {dates[0]:D}");
        Console.WriteLine($"   Sunrise: {dates[0]:T}");
        Console.WriteLine($"   Sunset:  {dates[1]:T}");
    }
}

// The example displays the following output:
//       Date: 11. listopada 2012.
//          Sunrise: 7:06:00
//          Sunset:  18:19:00
//
//       Date: 11 October 2012
//          Sunrise: 07:06:00
//          Sunset:  18:19:00
Imports System.Globalization
Imports System.Threading

Module Example3
    Dim dates() As Date = {New Date(2012, 10, 11, 7, 6, 0),
                            New Date(2012, 10, 11, 18, 19, 0)}

    Public Sub Main3()
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("hr-HR")
        ShowDayInfo()
        Console.WriteLine()
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB")
        ShowDayInfo()
    End Sub

    Private Sub ShowDayInfo()
        Console.WriteLine("Date: {0:D}", dates(0))
        Console.WriteLine("   Sunrise: {0:T}", dates(0))
        Console.WriteLine("   Sunset:  {0:T}", dates(1))
    End Sub
End Module
' The example displays the following output:
'       Date: 11. listopada 2012.
'          Sunrise: 7:06:00
'          Sunset:  18:19:00
'       
'       Date: 11 October 2012
'          Sunrise: 07:06:00
'          Sunset:  18:19:00

日付ず時刻を保持する

日付ず時刻のデヌタは、カルチャによっお異なる圢匏で保持しないでください。 これは䞀般的なプログラミング ゚ラヌで、デヌタが砎損しおいるか、ランタむム䟋倖が発生したす。 次の䟋では、英語 (米囜) カルチャの曞匏蚭定芏則を䜿甚しお、2013 幎 1 月 9 日ず 2013 幎 8 月 18 日の 2 ぀の日付を文字列ずしおシリアル化したす。 英語 (米囜) カルチャの芏則を䜿甚しおデヌタが取埗および解析されるず、正垞に埩元されたす。 ただし、英語 (英囜) カルチャの芏則を䜿甚しお取埗および解析されるず、最初の日付は 9 月 1 日ず誀っお解釈され、グレゎリオ暊には 18 月がないため、2 番目の日付は解析に倱敗したす。

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

public class Example4
{
    public static void Main4()
    {
        // Persist two dates as strings.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        DateTime[] dates = { new DateTime(2013, 1, 9),
                           new DateTime(2013, 8, 18) };
        StreamWriter sw = new StreamWriter("dateData.dat");
        sw.Write("{0:d}|{1:d}", dates[0], dates[1]);
        sw.Close();

        // Read the persisted data.
        StreamReader sr = new StreamReader("dateData.dat");
        string dateData = sr.ReadToEnd();
        sr.Close();
        string[] dateStrings = dateData.Split('|');

        // Restore and display the data using the conventions of the en-US culture.
        Console.WriteLine($"Current Culture: {Thread.CurrentThread.CurrentCulture.DisplayName}");
        foreach (var dateStr in dateStrings)
        {
            DateTime restoredDate;
            if (DateTime.TryParse(dateStr, out restoredDate))
                Console.WriteLine($"The date is {restoredDate:D}");
            else
                Console.WriteLine($"ERROR: Unable to parse {dateStr}");
        }
        Console.WriteLine();

        // Restore and display the data using the conventions of the en-GB culture.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
        Console.WriteLine($"Current Culture: {Thread.CurrentThread.CurrentCulture.DisplayName}");
        foreach (var dateStr in dateStrings)
        {
            DateTime restoredDate;
            if (DateTime.TryParse(dateStr, out restoredDate))
                Console.WriteLine($"The date is {restoredDate:D}");
            else
                Console.WriteLine($"ERROR: Unable to parse {dateStr}");
        }
    }
}

// The example displays the following output:
//       Current Culture: English (United States)
//       The date is Wednesday, January 09, 2013
//       The date is Sunday, August 18, 2013
//
//       Current Culture: English (United Kingdom)
//       The date is 01 September 2013
//       ERROR: Unable to parse 8/18/2013
Imports System.Globalization
Imports System.IO
Imports System.Threading

Module Example4
    Public Sub Main4()
        ' Persist two dates as strings.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        Dim dates() As DateTime = {New DateTime(2013, 1, 9),
                                    New DateTime(2013, 8, 18)}
        Dim sw As New StreamWriter("dateData.dat")
        sw.Write("{0:d}|{1:d}", dates(0), dates(1))
        sw.Close()

        ' Read the persisted data.
        Dim sr As New StreamReader("dateData.dat")
        Dim dateData As String = sr.ReadToEnd()
        sr.Close()
        Dim dateStrings() As String = dateData.Split("|"c)

        ' Restore and display the data using the conventions of the en-US culture.
        Console.WriteLine("Current Culture: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName)
        For Each dateStr In dateStrings
            Dim restoredDate As Date
            If Date.TryParse(dateStr, restoredDate) Then
                Console.WriteLine("The date is {0:D}", restoredDate)
            Else
                Console.WriteLine("ERROR: Unable to parse {0}", dateStr)
            End If
        Next
        Console.WriteLine()

        ' Restore and display the data using the conventions of the en-GB culture.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB")
        Console.WriteLine("Current Culture: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName)
        For Each dateStr In dateStrings
            Dim restoredDate As Date
            If Date.TryParse(dateStr, restoredDate) Then
                Console.WriteLine("The date is {0:D}", restoredDate)
            Else
                Console.WriteLine("ERROR: Unable to parse {0}", dateStr)
            End If
        Next
    End Sub
End Module
' The example displays the following output:
'       Current Culture: English (United States)
'       The date is Wednesday, January 09, 2013
'       The date is Sunday, August 18, 2013
'       
'       Current Culture: English (United Kingdom)
'       The date is 01 September 2013
'       ERROR: Unable to parse 8/18/2013

この問題は、次の 3 ぀の方法のいずれかで回避できたす。

  • 日付ず時刻を文字列ではなくバむナリ圢匏でシリアル化したす。
  • ナヌザヌのカルチャに関係なく同じカスタム曞匏指定文字列を䜿甚しお、日付ず時刻の文字列圢匏を保存しお解析したす。
  • むンバリアント カルチャの曞匏蚭定芏則を䜿甚しお、文字列を保存したす。

次の䟋は、最埌のアプロヌチを瀺しおいたす。 静的な CultureInfo.InvariantCulture プロパティによっお返されるむンバリアント カルチャの曞匏蚭定芏則を䜿甚したす。

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

public class Example5
{
    public static void Main5()
    {
        // Persist two dates as strings.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        DateTime[] dates = { new DateTime(2013, 1, 9),
                           new DateTime(2013, 8, 18) };
        StreamWriter sw = new StreamWriter("dateData.dat");
        sw.Write(String.Format(CultureInfo.InvariantCulture,
                               "{0:d}|{1:d}", dates[0], dates[1]));
        sw.Close();

        // Read the persisted data.
        StreamReader sr = new StreamReader("dateData.dat");
        string dateData = sr.ReadToEnd();
        sr.Close();
        string[] dateStrings = dateData.Split('|');

        // Restore and display the data using the conventions of the en-US culture.
        Console.WriteLine($"Current Culture: {Thread.CurrentThread.CurrentCulture.DisplayName}");
        foreach (var dateStr in dateStrings)
        {
            DateTime restoredDate;
            if (DateTime.TryParse(dateStr, CultureInfo.InvariantCulture,
                                  DateTimeStyles.None, out restoredDate))
                Console.WriteLine($"The date is {restoredDate:D}");
            else
                Console.WriteLine($"ERROR: Unable to parse {dateStr}");
        }
        Console.WriteLine();

        // Restore and display the data using the conventions of the en-GB culture.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
        Console.WriteLine($"Current Culture: {Thread.CurrentThread.CurrentCulture.DisplayName}");
        foreach (var dateStr in dateStrings)
        {
            DateTime restoredDate;
            if (DateTime.TryParse(dateStr, CultureInfo.InvariantCulture,
                                  DateTimeStyles.None, out restoredDate))
                Console.WriteLine($"The date is {restoredDate:D}");
            else
                Console.WriteLine($"ERROR: Unable to parse {dateStr}");
        }
    }
}

// The example displays the following output:
//       Current Culture: English (United States)
//       The date is Wednesday, January 09, 2013
//       The date is Sunday, August 18, 2013
//
//       Current Culture: English (United Kingdom)
//       The date is 09 January 2013
//       The date is 18 August 2013
Imports System.Globalization
Imports System.IO
Imports System.Threading

Module Example5
    Public Sub Main5()
        ' Persist two dates as strings.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        Dim dates() As DateTime = {New DateTime(2013, 1, 9),
                                    New DateTime(2013, 8, 18)}
        Dim sw As New StreamWriter("dateData.dat")
        sw.Write(String.Format(CultureInfo.InvariantCulture,
                               "{0:d}|{1:d}", dates(0), dates(1)))
        sw.Close()

        ' Read the persisted data.
        Dim sr As New StreamReader("dateData.dat")
        Dim dateData As String = sr.ReadToEnd()
        sr.Close()
        Dim dateStrings() As String = dateData.Split("|"c)

        ' Restore and display the data using the conventions of the en-US culture.
        Console.WriteLine("Current Culture: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName)
        For Each dateStr In dateStrings
            Dim restoredDate As Date
            If Date.TryParse(dateStr, CultureInfo.InvariantCulture,
                             DateTimeStyles.None, restoredDate) Then
                Console.WriteLine("The date is {0:D}", restoredDate)
            Else
                Console.WriteLine("ERROR: Unable to parse {0}", dateStr)
            End If
        Next
        Console.WriteLine()

        ' Restore and display the data using the conventions of the en-GB culture.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB")
        Console.WriteLine("Current Culture: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName)
        For Each dateStr In dateStrings
            Dim restoredDate As Date
            If Date.TryParse(dateStr, CultureInfo.InvariantCulture,
                             DateTimeStyles.None, restoredDate) Then
                Console.WriteLine("The date is {0:D}", restoredDate)
            Else
                Console.WriteLine("ERROR: Unable to parse {0}", dateStr)
            End If
        Next
    End Sub
End Module
' The example displays the following output:
'       Current Culture: English (United States)
'       The date is Wednesday, January 09, 2013
'       The date is Sunday, August 18, 2013
'       
'       Current Culture: English (United Kingdom)
'       The date is 09 January 2013
'       The date is 18 August 2013

シリアル化ずタむム ゟヌンの認識

日付ず時刻の倀には、䞀般的な時刻 ("The stores open on January 2, 2013, at 9:00 A.M.") から特定の時点 ("Date of birth: January 2, 2013 6:32:00 A.M.") たで、耇数の解釈を䜿甚できたす。 時刻倀が特定の時点を衚し、シリアル化された倀から埩元する堎合は、ナヌザヌの地理的な堎所やタむム ゟヌンに関係なく、同じ時点を衚すようにする必芁がありたす。

次の䟋は、この問題を瀺しおいたす。 1 ぀のロヌカルの日付ず時刻の倀を 3 ぀の 暙準圢匏の文字列ずしお保存したす。

  • 䞀般の日付ず長い圢匏の時刻の堎合は "G"。
  • 䞊べ替え可胜な日付/時刻の堎合は "s" です。
  • ラりンドトリップの日付/時刻の堎合は "o" です。
using System;
using System.IO;

public class Example6
{
    public static void Main6()
    {
        DateTime dateOriginal = new DateTime(2023, 3, 30, 18, 0, 0);
        dateOriginal = DateTime.SpecifyKind(dateOriginal, DateTimeKind.Local);

        // Serialize a date.
        if (!File.Exists("DateInfo.dat"))
        {
            StreamWriter sw = new StreamWriter("DateInfo.dat");
            sw.Write("{0:G}|{0:s}|{0:o}", dateOriginal);
            sw.Close();
            Console.WriteLine("Serialized dates to DateInfo.dat");
        }
        Console.WriteLine();

        // Restore the date from string values.
        StreamReader sr = new StreamReader("DateInfo.dat");
        string datesToSplit = sr.ReadToEnd();
        string[] dateStrings = datesToSplit.Split('|');
        foreach (var dateStr in dateStrings)
        {
            DateTime newDate = DateTime.Parse(dateStr);
            Console.WriteLine($"'{dateStr}' --> {newDate} {newDate.Kind}");
        }
    }
}
Imports System.IO

Module Example6
    Public Sub Main6()
        ' Serialize a date.
        Dim dateOriginal As Date = #03/30/2023 6:00PM#
        dateOriginal = DateTime.SpecifyKind(dateOriginal, DateTimeKind.Local)
        ' Serialize the date in string form.
        If Not File.Exists("DateInfo.dat") Then
            Dim sw As New StreamWriter("DateInfo.dat")
            sw.Write("{0:G}|{0:s}|{0:o}", dateOriginal)
            sw.Close()
        End If

        ' Restore the date from string values.
        Dim sr As New StreamReader("DateInfo.dat")
        Dim datesToSplit As String = sr.ReadToEnd()
        Dim dateStrings() As String = datesToSplit.Split("|"c)
        For Each dateStr In dateStrings
            Dim newDate As DateTime = DateTime.Parse(dateStr)
            Console.WriteLine("'{0}' --> {1} {2}",
                              dateStr, newDate, newDate.Kind)
        Next
    End Sub
End Module

シリアル化されたシステムず同じタむム ゟヌンのシステムでデヌタが埩元されるず、逆シリアル化された日付ず時刻の倀は、出力に瀺すように元の倀を正確に反映したす。

'3/30/2013 6:00:00 PM' --> 3/30/2013 6:00:00 PM Unspecified
'2013-03-30T18:00:00' --> 3/30/2013 6:00:00 PM Unspecified
'2013-03-30T18:00:00.0000000-07:00' --> 3/30/2013 6:00:00 PM Local

ただし、異なるタむム ゟヌンのシステム䞊のデヌタを埩元する堎合は、"o" (ラりンドトリップ) 暙準曞匏指定文字列で曞匏蚭定された日付ず時刻の倀のみがタむム ゟヌン情報を保持するため、同じむンスタント むン タむムを衚したす。 ロマンス暙準タむム ゟヌンのシステムで日付ず時刻のデヌタが埩元される堎合の出力を次に瀺したす。

'3/30/2023 6:00:00 PM' --> 3/30/2023 6:00:00 PM Unspecified
'2023-03-30T18:00:00' --> 3/30/2023 6:00:00 PM Unspecified
'2023-03-30T18:00:00.0000000-07:00' --> 3/31/2023 3:00:00 AM Local

デヌタが逆シリアル化されるシステムのタむム ゟヌンに関係なく、1 ぀の時刻を衚す日付ず時刻の倀を正確に反映するには、次のいずれかの操䜜を行いたす。

  • "o" (ラりンドトリップ) 暙準曞匏指定文字列を䜿甚しお、倀を文字列ずしお保存したす。 次に、タヌゲット システムで逆シリアル化したす。
  • それを UTC に倉換し、"r" (RFC1123) 暙準曞匏指定文字列を䜿甚しお文字列ずしお保存したす。 次に、タヌゲット システムで逆シリアル化し、ロヌカル時刻に倉換したす。
  • それを UTC に倉換し、"u" (汎甚䞊べ替え可胜) 暙準曞匏指定文字列を䜿甚しお文字列ずしお保存したす。 次に、タヌゲット システムで逆シリアル化し、ロヌカル時刻に倉換したす。

次の䟋は、各手法を瀺しおいたす。

using System;
using System.IO;

public class Example9
{
    public static void Main9()
    {
        // Serialize a date.
        DateTime dateOriginal = new DateTime(2023, 3, 30, 18, 0, 0);
        dateOriginal = DateTime.SpecifyKind(dateOriginal, DateTimeKind.Local);

        // Serialize the date in string form.
        if (!File.Exists("DateInfo2.dat"))
        {
            StreamWriter sw = new StreamWriter("DateInfo2.dat");
            sw.Write("{0:o}|{1:r}|{1:u}", dateOriginal,
                                          dateOriginal.ToUniversalTime());
            sw.Close();
        }

        // Restore the date from string values.
        StreamReader sr = new StreamReader("DateInfo2.dat");
        string datesToSplit = sr.ReadToEnd();
        string[] dateStrings = datesToSplit.Split('|');
        for (int ctr = 0; ctr < dateStrings.Length; ctr++)
        {
            DateTime newDate = DateTime.Parse(dateStrings[ctr]);
            if (ctr == 1)
            {
                Console.WriteLine($"'{dateStrings[ctr]}' --> {newDate} {newDate.Kind}");
            }
            else
            {
                DateTime newLocalDate = newDate.ToLocalTime();
                Console.WriteLine($"'{dateStrings[ctr]}' --> {newLocalDate} {newLocalDate.Kind}");
            }
        }
    }
}
Imports System.IO

Module Example9
    Public Sub Main9()
        ' Serialize a date.
        Dim dateOriginal As Date = #03/30/2023 6:00PM#
        dateOriginal = DateTime.SpecifyKind(dateOriginal, DateTimeKind.Local)

        ' Serialize the date in string form.
        If Not File.Exists("DateInfo2.dat") Then
            Dim sw As New StreamWriter("DateInfo2.dat")
            sw.Write("{0:o}|{1:r}|{1:u}", dateOriginal,
                                          dateOriginal.ToUniversalTime())
            sw.Close()
        End If

        ' Restore the date from string values.
        Dim sr As New StreamReader("DateInfo2.dat")
        Dim datesToSplit As String = sr.ReadToEnd()
        Dim dateStrings() As String = datesToSplit.Split("|"c)
        For ctr As Integer = 0 To dateStrings.Length - 1
            Dim newDate As DateTime = DateTime.Parse(dateStrings(ctr))
            If ctr = 1 Then
                Console.WriteLine("'{0}' --> {1} {2}",
                                  dateStrings(ctr), newDate, newDate.Kind)
            Else
                Dim newLocalDate As DateTime = newDate.ToLocalTime()
                Console.WriteLine("'{0}' --> {1} {2}",
                                  dateStrings(ctr), newLocalDate, newLocalDate.Kind)
            End If
        Next
    End Sub
End Module

デヌタが倪平掋暙準タむム ゟヌンのシステムでシリアル化され、ロマンス暙準タむム ゟヌンのシステムで逆シリアル化されるず、次の出力が衚瀺されたす。

'2023-03-30T18:00:00.0000000-07:00' --> 3/31/2023 3:00:00 AM Local
'Sun, 31 Mar 2023 01:00:00 GMT' --> 3/31/2023 3:00:00 AM Local
'2023-03-31 01:00:00Z' --> 3/31/2023 3:00:00 AM Local

詳现に぀いおは、「 タむム ゟヌン間の時刻の倉換」を参照しおください。

日付ず時刻の算術挔算を実行する

DateTime型ずDateTimeOffset型の䞡方で算術挔算がサポヌトされたす。 2 ぀の日付倀の差を蚈算するこずも、日付倀ずの間で特定の時間間隔を加算たたは枛算するこずもできたす。 ただし、日付ず時刻の倀に察する算術挔算では、タむム ゟヌンずタむム ゟヌン調敎ルヌルは考慮されたせん。 このため、時刻の瞬間を衚す倀に察する日付ず時刻の算術挔算では、䞍正確な結果が返される可胜性がありたす。

たずえば、倪平掋暙準時から倪平掋倏時間ぞの移行は、2013 幎の 3 月 10 日である 3 月の第 2 日曜日に行われたす。 次の䟋に瀺すように、倪平掋暙準時ゟヌンのシステムで 2013 幎 3 月 9 日午前 10 時 30 分から 48 時間埌の日付ず時刻を蚈算した堎合、2013 幎 3 月 11 日午前 10 時 30 分には、介入する時間調敎は考慮されたせん。

using System;

public class Example7
{
    public static void Main7()
    {
        DateTime date1 = DateTime.SpecifyKind(new DateTime(2013, 3, 9, 10, 30, 0),
                                              DateTimeKind.Local);
        TimeSpan interval = new TimeSpan(48, 0, 0);
        DateTime date2 = date1 + interval;
        Console.WriteLine($"{date1:g} + {interval.TotalHours:N1} hours = {date2:g}");
    }
}

// The example displays the following output:
//        3/9/2013 10:30 AM + 48.0 hours = 3/11/2013 10:30 AM
Module Example7
    Public Sub Main7()
        Dim date1 As Date = DateTime.SpecifyKind(#3/9/2013 10:30AM#,
                                                 DateTimeKind.Local)
        Dim interval As New TimeSpan(48, 0, 0)
        Dim date2 As Date = date1 + interval
        Console.WriteLine("{0:g} + {1:N1} hours = {2:g}",
                          date1, interval.TotalHours, date2)
    End Sub
End Module
' The example displays the following output:
'       3/9/2013 10:30 AM + 48.0 hours = 3/11/2013 10:30 AM

日付ず時刻の倀に察する算術挔算によっお正確な結果が埗られるようにするには、次の手順に埓いたす。

  1. ゜ヌス タむム ゟヌンの時刻を UTC に倉換したす。
  2. 算術挔算を実行したす。
  3. 結果が日付ず時刻の倀である堎合は、UTC から゜ヌス タむム ゟヌンの時刻に倉換したす。

次の䟋は前の䟋ず䌌おいたすが、2013 幎 3 月 9 日午前 10 時 30 分に 48 時間を正しく远加するには、次の 3 ぀の手順に埓いたす。

using System;

public class Example8
{
    public static void Main8()
    {
        TimeZoneInfo pst = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
        DateTime date1 = DateTime.SpecifyKind(new DateTime(2013, 3, 9, 10, 30, 0),
                                              DateTimeKind.Local);
        DateTime utc1 = date1.ToUniversalTime();
        TimeSpan interval = new TimeSpan(48, 0, 0);
        DateTime utc2 = utc1 + interval;
        DateTime date2 = TimeZoneInfo.ConvertTimeFromUtc(utc2, pst);
        Console.WriteLine($"{date1:g} + {interval.TotalHours:N1} hours = {date2:g}");
    }
}

// The example displays the following output:
//        3/9/2013 10:30 AM + 48.0 hours = 3/11/2013 11:30 AM
Module Example8
    Public Sub Main8()
        Dim pst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time")
        Dim date1 As Date = DateTime.SpecifyKind(#3/9/2013 10:30AM#,
                                                 DateTimeKind.Local)
        Dim utc1 As Date = date1.ToUniversalTime()
        Dim interval As New TimeSpan(48, 0, 0)
        Dim utc2 As Date = utc1 + interval
        Dim date2 As Date = TimeZoneInfo.ConvertTimeFromUtc(utc2, pst)
        Console.WriteLine("{0:g} + {1:N1} hours = {2:g}",
                          date1, interval.TotalHours, date2)
    End Sub
End Module
' The example displays the following output:
'       3/9/2013 10:30 AM + 48.0 hours = 3/11/2013 11:30 AM

詳现に぀いおは、「 日付ず時刻を䜿甚した算術挔算の実行」を参照しおください。

日付芁玠にカルチャに䟝存する名前を䜿甚する

アプリでは、月の名前たたは曜日の衚瀺が必芁になる堎合がありたす。 これを行うには、次のようなコヌドが䞀般的です。

using System;

public class Example12
{
   public static void Main12()
   {
      DateTime midYear = new DateTime(2013, 7, 1);
      Console.WriteLine($"{midYear:d} is a {GetDayName(midYear)}.");
   }

   private static string GetDayName(DateTime date)
   {
      return date.DayOfWeek.ToString("G");
   }
}

// The example displays the following output:
//        7/1/2013 is a Monday.
Module Example12
    Public Sub Main12()
        Dim midYear As Date = #07/01/2013#
        Console.WriteLine("{0:d} is a {1}.", midYear, GetDayName(midYear))
    End Sub

    Private Function GetDayName(dat As Date) As String
        Return dat.DayOfWeek.ToString("G")
    End Function
End Module
' The example displays the following output:
'       7/1/2013 is a Monday.

ただし、このコヌドは垞に曜日の名前を英語で返したす。 月の名前を抜出するコヌドは、倚くの堎合、さらに柔軟性が高くありたせん。 倚くの堎合、特定の蚀語の月の名前を持぀ 12 か月のカレンダヌを想定しおいたす。

次の䟋に瀺すように 、カスタムの日時曞匏指定文字列 たたは DateTimeFormatInfo オブゞェクトのプロパティを䜿甚するず、ナヌザヌのカルチャで曜日たたは月の名前を反映する文字列を簡単に抜出できたす。 この䟋では、珟圚のカルチャをフランス語 (フランス) に倉曎し、2013 幎 7 月 1 日の曜日の名前ず月の名前を衚瀺したす。

using System;
using System.Globalization;

public class Example13
{
    public static void Main13()
    {
        // Set the current culture to French (France).
        CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");

        DateTime midYear = new DateTime(2013, 7, 1);
        Console.WriteLine($"{midYear:d} is a {DateUtilities.GetDayName(midYear)}.");
        Console.WriteLine($"{midYear:d} is a {DateUtilities.GetDayName((int)midYear.DayOfWeek)}.");
        Console.WriteLine($"{midYear:d} is in {DateUtilities.GetMonthName(midYear)}.");
        Console.WriteLine($"{midYear:d} is in {DateUtilities.GetMonthName(midYear.Month)}.");
    }
}

public static class DateUtilities
{
    public static string GetDayName(int dayOfWeek)
    {
        if (dayOfWeek < 0 | dayOfWeek > DateTimeFormatInfo.CurrentInfo.DayNames.Length)
            return String.Empty;
        else
            return DateTimeFormatInfo.CurrentInfo.DayNames[dayOfWeek];
    }

    public static string GetDayName(DateTime date)
    {
        return date.ToString("dddd");
    }

    public static string GetMonthName(int month)
    {
        if (month < 1 | month > DateTimeFormatInfo.CurrentInfo.MonthNames.Length - 1)
            return String.Empty;
        else
            return DateTimeFormatInfo.CurrentInfo.MonthNames[month - 1];
    }

    public static string GetMonthName(DateTime date)
    {
        return date.ToString("MMMM");
    }
}

// The example displays the following output:
//       01/07/2013 is a lundi.
//       01/07/2013 is a lundi.
//       01/07/2013 is in juillet.
//       01/07/2013 is in juillet.
Imports System.Globalization
Imports System.Threading

Module Example13
    Public Sub Main13()
        ' Set the current culture to French (France).
        CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR")

        Dim midYear As Date = #07/01/2013#
        Console.WriteLine("{0:d} is a {1}.", midYear, DateUtilities.GetDayName(midYear))
        Console.WriteLine("{0:d} is a {1}.", midYear, DateUtilities.GetDayName(midYear.DayOfWeek))
        Console.WriteLine("{0:d} is in {1}.", midYear, DateUtilities.GetMonthName(midYear))
        Console.WriteLine("{0:d} is in {1}.", midYear, DateUtilities.GetMonthName(midYear.Month))
    End Sub
End Module

Public Class DateUtilities
    Public Shared Function GetDayName(dayOfWeek As Integer) As String
        If dayOfWeek < 0 Or dayOfWeek > DateTimeFormatInfo.CurrentInfo.DayNames.Length Then
            Return String.Empty
        Else
            Return DateTimeFormatInfo.CurrentInfo.DayNames(dayOfWeek)
        End If
    End Function

    Public Shared Function GetDayName(dat As Date) As String
        Return dat.ToString("dddd")
    End Function

    Public Shared Function GetMonthName(month As Integer) As String
        If month < 1 Or month > DateTimeFormatInfo.CurrentInfo.MonthNames.Length - 1 Then
            Return String.Empty
        Else
            Return DateTimeFormatInfo.CurrentInfo.MonthNames(month - 1)
        End If
    End Function

    Public Shared Function GetMonthName(dat As Date) As String
        Return dat.ToString("MMMM")
    End Function
End Class
' The example displays the following output:
'       01/07/2013 is a lundi.
'       01/07/2013 is a lundi.
'       01/07/2013 is in juillet.
'       01/07/2013 is in juillet.

数倀

数倀の凊理は、ナヌザヌ むンタヌフェむスに衚瀺されるか、氞続化されおいるかによっお異なりたす。 このセクションでは、䞡方の䜿甚法に぀いお説明したす。

泚

解析および曞匏蚭定操䜜では、.NET では、基本ラテン文字 0 から 9 (U+0030 から U+0039) のみが数字ずしお認識されたす。

数倀を衚瀺する

通垞、ナヌザヌ むンタヌフェむスに数倀が衚瀺される堎合は、ナヌザヌのカルチャの曞匏蚭定芏則を䜿甚する必芁がありたす。これは、CultureInfo.CurrentCulture プロパティず、NumberFormatInfo プロパティによっお返されるCultureInfo.CurrentCulture.NumberFormat オブゞェクトによっお定矩されたす。 珟圚のカルチャの曞匏蚭定芏則は、次の方法で日付を曞匏蚭定するずきに自動的に䜿甚されたす。

  • 任意の数倀型のパラメヌタヌなしの ToString メ゜ッドを䜿甚したす。
  • 匕数ずしお曞匏指定文字列を含む任意の数倀型の ToString(String) メ゜ッドを䜿甚したす。
  • 数倀 を䜿甚した耇合曞匏 の䜿甚。

次の䜿甚䟋は、パリ, フランス の 1 ヶ月あたりの平均枩床を衚瀺したす。 デヌタを衚瀺する前に、最初に珟圚のカルチャをフランス語 (フランス) に蚭定し、次に英語 (米囜) に蚭定したす。 いずれの堎合も、月の名前ず枩床は、そのカルチャに適した圢匏で衚瀺されたす。 2 ぀のカルチャでは、枩床倀に異なる小数点区切り蚘号が䜿甚されるこずに泚意しおください。 たた、この䟋では、"MMMM" カスタムの日付ず時刻の曞匏指定文字列を䜿甚しお完党な月の名前を衚瀺し、 DateTimeFormatInfo.MonthNames 配列内の最も長い月の名前の長さを決定するこずによっお、結果文字列の月名に適切な量の領域を割り圓おるこずにも泚意しおください。

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

public class Example14
{
    public static void Main14()
    {
        DateTime dateForMonth = new DateTime(2013, 1, 1);
        double[] temperatures = {  3.4, 3.5, 7.6, 10.4, 14.5, 17.2,
                                19.9, 18.2, 15.9, 11.3, 6.9, 5.3 };

        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
        Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.DisplayName}");
        // Build the format string dynamically so we allocate enough space for the month name.
        string fmtString = "{0,-" + GetLongestMonthNameLength().ToString() + ":MMMM}     {1,4}";
        for (int ctr = 0; ctr < temperatures.Length; ctr++)
            Console.WriteLine(fmtString,
                              dateForMonth.AddMonths(ctr),
                              temperatures[ctr]);

        Console.WriteLine();

        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.DisplayName}");
        fmtString = "{0,-" + GetLongestMonthNameLength().ToString() + ":MMMM}     {1,4}";
        for (int ctr = 0; ctr < temperatures.Length; ctr++)
            Console.WriteLine(fmtString,
                              dateForMonth.AddMonths(ctr),
                              temperatures[ctr]);
    }

    private static int GetLongestMonthNameLength()
    {
        int length = 0;
        foreach (var nameOfMonth in DateTimeFormatInfo.CurrentInfo.MonthNames)
            if (nameOfMonth.Length > length) length = nameOfMonth.Length;

        return length;
    }
}

// The example displays the following output:
//    Current Culture: French (France)
//       janvier        3,4
//       février        3,5
//       mars           7,6
//       avril         10,4
//       mai           14,5
//       juin          17,2
//       juillet       19,9
//       août          18,2
//       septembre     15,9
//       octobre       11,3
//       novembre       6,9
//       décembre       5,3
//
//       Current Culture: English (United States)
//       January        3.4
//       February       3.5
//       March          7.6
//       April         10.4
//       May           14.5
//       June          17.2
//       July          19.9
//       August        18.2
//       September     15.9
//       October       11.3
//       November       6.9
//       December       5.3
Imports System.Globalization
Imports System.Threading

Module Example14
    Public Sub Main14()
        Dim dateForMonth As Date = #1/1/2013#
        Dim temperatures() As Double = {3.4, 3.5, 7.6, 10.4, 14.5, 17.2,
                                         19.9, 18.2, 15.9, 11.3, 6.9, 5.3}

        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR")
        Console.WriteLine("Current Culture: {0}", CultureInfo.CurrentCulture.DisplayName)
        Dim fmtString As String = "{0,-" + GetLongestMonthNameLength().ToString() + ":MMMM}     {1,4}"
        For ctr = 0 To temperatures.Length - 1
            Console.WriteLine(fmtString,
                              dateForMonth.AddMonths(ctr),
                              temperatures(ctr))
        Next
        Console.WriteLine()

        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        Console.WriteLine("Current Culture: {0}", CultureInfo.CurrentCulture.DisplayName)
        ' Build the format string dynamically so we allocate enough space for the month name.
        fmtString = "{0,-" + GetLongestMonthNameLength().ToString() + ":MMMM}     {1,4}"
        For ctr = 0 To temperatures.Length - 1
            Console.WriteLine(fmtString,
                              dateForMonth.AddMonths(ctr),
                              temperatures(ctr))
        Next
    End Sub

    Private Function GetLongestMonthNameLength() As Integer
        Dim length As Integer
        For Each nameOfMonth In DateTimeFormatInfo.CurrentInfo.MonthNames
            If nameOfMonth.Length > length Then length = nameOfMonth.Length
        Next
        Return length
    End Function
End Module
' The example displays the following output:
'       Current Culture: French (France)
'       janvier        3,4
'       février        3,5
'       mars           7,6
'       avril         10,4
'       mai           14,5
'       juin          17,2
'       juillet       19,9
'       août          18,2
'       septembre     15,9
'       octobre       11,3
'       novembre       6,9
'       décembre       5,3
'       
'       Current Culture: English (United States)
'       January        3.4
'       February       3.5
'       March          7.6
'       April         10.4
'       May           14.5
'       June          17.2
'       July          19.9
'       August        18.2
'       September     15.9
'       October       11.3
'       November       6.9
'       December       5.3

数倀を保持する

カルチャ固有の圢匏で数倀デヌタを保持しないでください。 これは䞀般的なプログラミング ゚ラヌで、デヌタが砎損しおいるか、ランタむム䟋倖が発生したす。 次の䟋では、10 個のランダムな浮動小数点数を生成し、英語 (米囜) カルチャの曞匏蚭定芏則を䜿甚しお文字列ずしおシリアル化したす。 英語 (米囜) カルチャの芏則を䜿甚しおデヌタが取埗および解析されるず、正垞に埩元されたす。 ただし、フランス語 (フランス) カルチャの芏則を䜿甚しお取埗および解析する堎合、カルチャで異なる小数点区切り蚘号が䜿甚されるため、どの数倀も解析できたせん。

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

public class Example15
{
    public static void Main15()
    {
        // Create ten random doubles.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        double[] numbers = GetRandomNumbers(10);
        DisplayRandomNumbers(numbers);

        // Persist the numbers as strings.
        StreamWriter sw = new StreamWriter("randoms.dat");
        for (int ctr = 0; ctr < numbers.Length; ctr++)
            sw.Write("{0:R}{1}", numbers[ctr], ctr < numbers.Length - 1 ? "|" : "");

        sw.Close();

        // Read the persisted data.
        StreamReader sr = new StreamReader("randoms.dat");
        string numericData = sr.ReadToEnd();
        sr.Close();
        string[] numberStrings = numericData.Split('|');

        // Restore and display the data using the conventions of the en-US culture.
        Console.WriteLine($"Current Culture: {Thread.CurrentThread.CurrentCulture.DisplayName}");
        foreach (var numberStr in numberStrings)
        {
            double restoredNumber;
            if (Double.TryParse(numberStr, out restoredNumber))
                Console.WriteLine(restoredNumber.ToString("R"));
            else
                Console.WriteLine($"ERROR: Unable to parse '{numberStr}'");
        }
        Console.WriteLine();

        // Restore and display the data using the conventions of the fr-FR culture.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
        Console.WriteLine($"Current Culture: {Thread.CurrentThread.CurrentCulture.DisplayName}");
        foreach (var numberStr in numberStrings)
        {
            double restoredNumber;
            if (Double.TryParse(numberStr, out restoredNumber))
                Console.WriteLine(restoredNumber.ToString("R"));
            else
                Console.WriteLine($"ERROR: Unable to parse '{numberStr}'");
        }
    }

    private static double[] GetRandomNumbers(int n)
    {
        Random rnd = new Random();
        double[] numbers = new double[n];
        for (int ctr = 0; ctr < n; ctr++)
            numbers[ctr] = rnd.NextDouble() * 1000;
        return numbers;
    }

    private static void DisplayRandomNumbers(double[] numbers)
    {
        for (int ctr = 0; ctr < numbers.Length; ctr++)
            Console.WriteLine(numbers[ctr].ToString("R"));
        Console.WriteLine();
    }
}

// The example displays output like the following:
//       487.0313743534644
//       674.12000879371533
//       498.72077885024288
//       42.3034229512808
//       970.57311049223563
//       531.33717716268131
//       587.82905693530529
//       562.25210175023039
//       600.7711019370571
//       299.46113717717174
//
//       Current Culture: English (United States)
//       487.0313743534644
//       674.12000879371533
//       498.72077885024288
//       42.3034229512808
//       970.57311049223563
//       531.33717716268131
//       587.82905693530529
//       562.25210175023039
//       600.7711019370571
//       299.46113717717174
//
//       Current Culture: French (France)
//       ERROR: Unable to parse '487.0313743534644'
//       ERROR: Unable to parse '674.12000879371533'
//       ERROR: Unable to parse '498.72077885024288'
//       ERROR: Unable to parse '42.3034229512808'
//       ERROR: Unable to parse '970.57311049223563'
//       ERROR: Unable to parse '531.33717716268131'
//       ERROR: Unable to parse '587.82905693530529'
//       ERROR: Unable to parse '562.25210175023039'
//       ERROR: Unable to parse '600.7711019370571'
//       ERROR: Unable to parse '299.46113717717174'
Imports System.Globalization
Imports System.IO
Imports System.Threading

Module Example15
    Public Sub Main15()
        ' Create ten random doubles.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        Dim numbers() As Double = GetRandomNumbers(10)
        DisplayRandomNumbers(numbers)

        ' Persist the numbers as strings.
        Dim sw As New StreamWriter("randoms.dat")
        For ctr As Integer = 0 To numbers.Length - 1
            sw.Write("{0:R}{1}", numbers(ctr), If(ctr < numbers.Length - 1, "|", ""))
        Next
        sw.Close()

        ' Read the persisted data.
        Dim sr As New StreamReader("randoms.dat")
        Dim numericData As String = sr.ReadToEnd()
        sr.Close()
        Dim numberStrings() As String = numericData.Split("|"c)

        ' Restore and display the data using the conventions of the en-US culture.
        Console.WriteLine("Current Culture: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName)
        For Each numberStr In numberStrings
            Dim restoredNumber As Double
            If Double.TryParse(numberStr, restoredNumber) Then
                Console.WriteLine(restoredNumber.ToString("R"))
            Else
                Console.WriteLine("ERROR: Unable to parse '{0}'", numberStr)
            End If
        Next
        Console.WriteLine()

        ' Restore and display the data using the conventions of the fr-FR culture.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR")
        Console.WriteLine("Current Culture: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName)
        For Each numberStr In numberStrings
            Dim restoredNumber As Double
            If Double.TryParse(numberStr, restoredNumber) Then
                Console.WriteLine(restoredNumber.ToString("R"))
            Else
                Console.WriteLine("ERROR: Unable to parse '{0}'", numberStr)
            End If
        Next
    End Sub

    Private Function GetRandomNumbers(n As Integer) As Double()
        Dim rnd As New Random()
        Dim numbers(n - 1) As Double
        For ctr As Integer = 0 To n - 1
            numbers(ctr) = rnd.NextDouble * 1000
        Next
        Return numbers
    End Function

    Private Sub DisplayRandomNumbers(numbers As Double())
        For ctr As Integer = 0 To numbers.Length - 1
            Console.WriteLine(numbers(ctr).ToString("R"))
        Next
        Console.WriteLine()
    End Sub
End Module
' The example displays output like the following:
'       487.0313743534644
'       674.12000879371533
'       498.72077885024288
'       42.3034229512808
'       970.57311049223563
'       531.33717716268131
'       587.82905693530529
'       562.25210175023039
'       600.7711019370571
'       299.46113717717174
'       
'       Current Culture: English (United States)
'       487.0313743534644
'       674.12000879371533
'       498.72077885024288
'       42.3034229512808
'       970.57311049223563
'       531.33717716268131
'       587.82905693530529
'       562.25210175023039
'       600.7711019370571
'       299.46113717717174
'       
'       Current Culture: French (France)
'       ERROR: Unable to parse '487.0313743534644'
'       ERROR: Unable to parse '674.12000879371533'
'       ERROR: Unable to parse '498.72077885024288'
'       ERROR: Unable to parse '42.3034229512808'
'       ERROR: Unable to parse '970.57311049223563'
'       ERROR: Unable to parse '531.33717716268131'
'       ERROR: Unable to parse '587.82905693530529'
'       ERROR: Unable to parse '562.25210175023039'
'       ERROR: Unable to parse '600.7711019370571'
'       ERROR: Unable to parse '299.46113717717174'

この問題を回避するには、次のいずれかの手法を䜿甚したす。

  • ナヌザヌのカルチャに関係なく同じカスタム曞匏指定文字列を䜿甚しお、数倀の文字列圢匏を保存しお解析したす。
  • CultureInfo.InvariantCulture プロパティによっお返されるむンバリアント カルチャの曞匏蚭定芏則を䜿甚しお、数倀を文字列ずしお保存したす。

通貚倀のシリアル化は特殊なケヌスです。 通貚倀は衚珟される通貚の単䜍に䟝存するため、独立した数倀ずしお扱うのはほずんど意味がありたせん。 ただし、通貚蚘号を含む曞匏蚭定された文字列ずしお通貚倀を保存する堎合、次の䟋に瀺すように、既定のカルチャで別の通貚蚘号が䜿甚されおいるシステムでは逆シリアル化できたせん。

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

public class Example1
{
   public static void Main1()
   {
      // Display the currency value.
      Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
      Decimal value = 16039.47m;
      Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.DisplayName}");
      Console.WriteLine($"Currency Value: {value:C2}");

      // Persist the currency value as a string.
      StreamWriter sw = new StreamWriter("currency.dat");
      sw.Write(value.ToString("C2"));
      sw.Close();

      // Read the persisted data using the current culture.
      StreamReader sr = new StreamReader("currency.dat");
      string currencyData = sr.ReadToEnd();
      sr.Close();

      // Restore and display the data using the conventions of the current culture.
      Decimal restoredValue;
      if (Decimal.TryParse(currencyData, out restoredValue))
         Console.WriteLine(restoredValue.ToString("C2"));
      else
         Console.WriteLine($"ERROR: Unable to parse '{currencyData}'");
      Console.WriteLine();

      // Restore and display the data using the conventions of the en-GB culture.
      Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
      Console.WriteLine($"Current Culture: {Thread.CurrentThread.CurrentCulture.DisplayName}");
      if (Decimal.TryParse(currencyData, NumberStyles.Currency, null, out restoredValue))
         Console.WriteLine(restoredValue.ToString("C2"));
      else
         Console.WriteLine($"ERROR: Unable to parse '{currencyData}'");
      Console.WriteLine();
   }
}
// The example displays output like the following:
//       Current Culture: English (United States)
//       Currency Value: $16,039.47
//       ERROR: Unable to parse '$16,039.47'
//
//       Current Culture: English (United Kingdom)
//       ERROR: Unable to parse '$16,039.47'
Imports System.Globalization
Imports System.IO
Imports System.Threading

Module Example1
    Public Sub Main1()
        ' Display the currency value.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        Dim value As Decimal = 16039.47D
        Console.WriteLine("Current Culture: {0}", CultureInfo.CurrentCulture.DisplayName)
        Console.WriteLine("Currency Value: {0:C2}", value)

        ' Persist the currency value as a string.
        Dim sw As New StreamWriter("currency.dat")
        sw.Write(value.ToString("C2"))
        sw.Close()

        ' Read the persisted data using the current culture.
        Dim sr As New StreamReader("currency.dat")
        Dim currencyData As String = sr.ReadToEnd()
        sr.Close()

        ' Restore and display the data using the conventions of the current culture.
        Dim restoredValue As Decimal
        If Decimal.TryParse(currencyData, restoredValue) Then
            Console.WriteLine(restoredValue.ToString("C2"))
        Else
            Console.WriteLine("ERROR: Unable to parse '{0}'", currencyData)
        End If
        Console.WriteLine()

        ' Restore and display the data using the conventions of the en-GB culture.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB")
        Console.WriteLine("Current Culture: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName)
        If Decimal.TryParse(currencyData, NumberStyles.Currency, Nothing, restoredValue) Then
            Console.WriteLine(restoredValue.ToString("C2"))
        Else
            Console.WriteLine("ERROR: Unable to parse '{0}'", currencyData)
        End If
        Console.WriteLine()
    End Sub
End Module
' The example displays output like the following:
'       Current Culture: English (United States)
'       Currency Value: $16,039.47
'       ERROR: Unable to parse '$16,039.47'
'       
'       Current Culture: English (United Kingdom)
'       ERROR: Unable to parse '$16,039.47'

代わりに、珟圚のカルチャずは別に倀ずその通貚蚘号を逆シリアル化できるように、カルチャの名前などのカルチャ情報ず共に数倀をシリアル化する必芁がありたす。 次の䟋では、CurrencyValue倀ず倀が属するカルチャの名前ずいう 2 ぀のメンバヌを持぀Decimal構造䜓を定矩したす。

using System;
using System.Globalization;
using System.Text.Json;
using System.Threading;

public class Example2
{
    public static void Main2()
    {
        // Display the currency value.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        Decimal value = 16039.47m;
        Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.DisplayName}");
        Console.WriteLine($"Currency Value: {value:C2}");

        // Serialize the currency data.
        CurrencyValue data = new()
        {
            Amount = value,
            CultureName = CultureInfo.CurrentCulture.Name
        };
        string serialized = JsonSerializer.Serialize(data);
        Console.WriteLine();

        // Change the current culture.
        CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
        Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.DisplayName}");

        // Deserialize the data.
        CurrencyValue restoredData = JsonSerializer.Deserialize<CurrencyValue>(serialized);

        // Display the round-tripped value.
        CultureInfo culture = CultureInfo.CreateSpecificCulture(restoredData.CultureName);
        Console.WriteLine($"Currency Value: {restoredData.Amount.ToString("C2", culture)}");
    }
}

internal struct CurrencyValue
{
    public decimal Amount { get; set; }
    public string CultureName { get; set; }
}

// The example displays the following output:
//       Current Culture: English (United States)
//       Currency Value: $16,039.47
//
//       Current Culture: English (United Kingdom)
//       Currency Value: $16,039.47
Imports System.Globalization
Imports System.Text.Json
Imports System.Threading

Friend Structure CurrencyValue
    Public Property Amount As Decimal
    Public Property CultureName As String
End Structure

Module Example2
    Public Sub Main2()
        ' Display the currency value.
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        Dim value As Decimal = 16039.47D
        Console.WriteLine("Current Culture: {0}", CultureInfo.CurrentCulture.DisplayName)
        Console.WriteLine("Currency Value: {0:C2}", value)

        ' Serialize the currency data.
        Dim data As New CurrencyValue With {
            .Amount = value,
            .CultureName = CultureInfo.CurrentCulture.Name
        }

        Dim serialized As String = JsonSerializer.Serialize(data)
        Console.WriteLine()

        ' Change the current culture.
        CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB")
        Console.WriteLine("Current Culture: {0}", CultureInfo.CurrentCulture.DisplayName)

        ' Deserialize the data.
        Dim restoredData As CurrencyValue = JsonSerializer.Deserialize(Of CurrencyValue)(serialized)

        ' Display the round-tripped value.
        Dim culture As CultureInfo = CultureInfo.CreateSpecificCulture(restoredData.CultureName)
        Console.WriteLine("Currency Value: {0}", restoredData.Amount.ToString("C2", culture))
    End Sub
End Module

' The example displays the following output:
'       Current Culture: English (United States)
'       Currency Value: $16,039.47
'       
'       Current Culture: English (United Kingdom)
'       Currency Value: $16,039.47

カルチャ固有の蚭定を䜿甚する

.NET では、 CultureInfo クラスは特定のカルチャたたはリヌゞョンを衚したす。 そのプロパティの䞀郚は、カルチャの䞀郚の偎面に関する特定の情報を提䟛するオブゞェクトを返したす。

  • CultureInfo.CompareInfo プロパティは、カルチャが文字列を比范および順序付けする方法に関する情報を含むCompareInfo オブゞェクトを返したす。

  • CultureInfo.DateTimeFormat プロパティは、日付ず時刻のデヌタの曞匏蚭定に䜿甚されるカルチャ固有の情報を提䟛するDateTimeFormatInfo オブゞェクトを返したす。

  • CultureInfo.NumberFormat プロパティは、数倀デヌタの曞匏蚭定に䜿甚されるカルチャ固有の情報を提䟛するNumberFormatInfo オブゞェクトを返したす。

  • CultureInfo.TextInfo プロパティは、カルチャの文字システムに関する情報を提䟛するTextInfo オブゞェクトを返したす。

䞀般に、特定の CultureInfo プロパティずその関連オブゞェクトの倀に関する想定は䞀切行わないでください。 代わりに、次の理由により、カルチャ固有のデヌタを倉曎の察象ずしお衚瀺する必芁がありたす。

  • 個々のプロパティ倀は、デヌタの修正、デヌタの改善、たたはカルチャ固有の芏則の倉曎に䌎い、時間の経過ずずもに倉曎されるこずがありたす。

  • 個々のプロパティ倀は、.NET たたはオペレヌティング システムのバヌゞョンによっお異なる堎合がありたす。

  • .NET では、眮換カルチャがサポヌトされおいたす。 これにより、既存の暙準カルチャを補完するか、既存の暙準カルチャを完党に眮き換える新しいカスタム カルチャを定矩できたす。

  • Windows システムでは、ナヌザヌはコントロヌル パネルの [地域ず蚀語 ] アプリを䜿甚しお、カルチャ固有の蚭定をカスタマむズできたす。 CultureInfo オブゞェクトをむンスタンス化するずきに、CultureInfo(String, Boolean) コンストラクタヌを呌び出すこずによっお、これらのナヌザヌのカスタマむズが反映されおいるかどうかを刀断できたす。 通垞、゚ンド ナヌザヌ アプリの堎合は、ナヌザヌが期埅する圢匏でデヌタが衚瀺されるように、ナヌザヌ蚭定を尊重する必芁がありたす。

こちらも参照ください