Bagikan melalui


Globalisasi

Globalisasi melibatkan merancang dan mengembangkan aplikasi siap-dunia yang mendukung antarmuka lokal dan data regional untuk pengguna dalam beberapa budaya. Sebelum memulai fase desain, Anda harus menentukan budaya mana yang akan didukung aplikasi Anda. Meskipun aplikasi menargetkan satu budaya atau wilayah sebagai defaultnya, Anda dapat merancang dan menulisnya sehingga dapat dengan mudah diperluas kepada pengguna di budaya atau wilayah lain.

Sebagai pengembang, kita semua memiliki asumsi tentang antarmuka pengguna dan data yang dibentuk oleh budaya kita. Misalnya, untuk pengembang berbahasa Inggris di Amerika Serikat, menserialisasikan data tanggal dan waktu sebagai string dalam format MM/dd/yyyy hh:mm:ss tampaknya sangat masuk akal. Namun, deserialisasi bahwa string pada sistem dalam budaya yang berbeda kemungkinan akan melemparkan pengecualian atau menghasilkan data yang FormatException tidak akurat. Globalisasi memungkinkan kami mengidentifikasi asumsi khusus budaya tersebut dan memastikan bahwa asumsi tersebut tidak memengaruhi desain atau kode aplikasi kami.

Artikel ini membahas beberapa masalah utama yang harus Anda pertimbangkan dan praktik terbaik yang dapat Anda ikuti saat menangani string, nilai tanggal dan waktu, dan nilai numerik dalam aplikasi global.

String

Penanganan karakter dan string adalah fokus utama globalisasi, karena setiap budaya atau wilayah dapat menggunakan karakter dan set karakter yang berbeda dan mengurutkannya secara berbeda. Bagian ini memberikan rekomendasi untuk menggunakan string di aplikasi global.

Menggunakan Unicode secara internal

Secara default, .NET menggunakan string Unicode. String Unicode terdiri dari nol, satu, atau beberapa Char objek, yang masing-masing mewakili unit kode UTF-16. Ada representasi Unicode untuk hampir setiap karakter dalam setiap karakter yang ditetapkan yang digunakan di seluruh dunia.

Banyak aplikasi dan sistem operasi, termasuk sistem operasi Windows, juga dapat menggunakan halaman kode untuk mewakili set karakter. Halaman kode biasanya berisi nilai ASCII standar dari 0x00 melalui 0x7F dan memetakan karakter lain ke nilai yang tersisa dari 0x80 hingga 0xFF. Interpretasi nilai dari 0x80 melalui 0xFF tergantung pada halaman kode tertentu. Karena itu, Anda harus menghindari penggunaan halaman kode di aplikasi global jika memungkinkan.

Contoh berikut mengilustrasikan bahaya menafsirkan data halaman kode ketika halaman kode default pada sistem berbeda dari halaman kode tempat data disimpan. (Untuk mensimulasikan skenario ini, contoh secara eksplisit menentukan halaman kode yang berbeda.) Pertama, contoh mendefinisikan array yang terdiri dari karakter huruf besar alfabet Yunani. Ini mengodekannya ke dalam array byte dengan menggunakan halaman kode 737 (juga dikenal sebagai MS-DOS Yunani) dan menyimpan array byte ke file. Jika file diambil dan array byte-nya didekodekan dengan menggunakan halaman kode 737, karakter asli akan dipulihkan. Namun, jika file diambil dan array byte-nya didekodekan dengan menggunakan halaman kode 1252 (atau Windows-1252, yang mewakili karakter dalam alfabet Latin), karakter asli akan hilang.

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:
'       ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ
'       €‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’""•–—

Penggunaan Unicode memastikan bahwa unit kode yang sama selalu memetakan ke karakter yang sama, dan bahwa karakter yang sama selalu memetakan ke array byte yang sama.

Menggunakan file sumber daya

Bahkan jika Anda mengembangkan aplikasi yang menargetkan satu budaya atau wilayah, Anda harus menggunakan file sumber daya untuk menyimpan string dan sumber daya lain yang ditampilkan di antarmuka pengguna. Anda tidak boleh menambahkannya langsung ke kode Anda. Menggunakan file sumber daya memiliki sejumlah keuntungan:

  • Semua string berada dalam satu lokasi. Anda tidak perlu mencari di seluruh kode sumber Anda untuk mengidentifikasi string yang akan dimodifikasi untuk bahasa atau budaya tertentu.
  • Tidak perlu menduplikasi string. Pengembang yang tidak menggunakan file sumber daya sering menentukan string yang sama dalam beberapa file kode sumber. Duplikasi ini meningkatkan probabilitas bahwa satu atau beberapa instans akan diabaikan ketika string dimodifikasi.
  • Anda dapat menyertakan sumber daya non-string, seperti gambar atau data biner, dalam file sumber daya alih-alih menyimpannya dalam file mandiri terpisah, sehingga dapat diambil dengan mudah.

Menggunakan file sumber daya memiliki keuntungan khusus jika Anda membuat aplikasi yang dilokalkan. Saat Anda menyebarkan sumber daya di rakitan satelit, runtime bahasa umum secara otomatis memilih sumber daya yang sesuai budaya berdasarkan budaya UI pengguna saat ini seperti yang didefinisikan oleh CultureInfo.CurrentUICulture properti . Selama Anda menyediakan sumber daya khusus budaya yang sesuai dan membuat instans ResourceManager objek dengan benar atau menggunakan kelas sumber daya yang diketik dengan kuat, runtime menangani detail pengambilan sumber daya yang sesuai.

Untuk informasi selengkapnya tentang membuat file sumber daya, lihat Membuat file sumber daya. Untuk informasi tentang membuat dan menyebarkan rakitan satelit, lihat Membuat rakitan satelit dan Mengemas dan Menyebarkan sumber daya.

Mencari dan membandingkan string

Jika memungkinkan, Anda harus menangani string sebagai seluruh string alih-alih menanganinya sebagai serangkaian karakter individual. Ini sangat penting ketika Anda mengurutkan atau mencari substring, untuk mencegah masalah yang terkait dengan penguraian karakter gabungan.

Tip

Anda dapat menggunakan StringInfo kelas untuk bekerja dengan elemen teks daripada karakter individual dalam string.

Dalam pencarian string dan perbandingan, kesalahan umum adalah memperlakukan string sebagai kumpulan karakter, yang masing-masing diwakili oleh Char objek. Bahkan, satu karakter dapat dibentuk oleh satu, dua, atau lebih Char objek. Karakter tersebut paling sering ditemukan dalam string dari budaya yang alfabetnya terdiri dari karakter di luar rentang karakter Unicode Basic Latin (U+0021 hingga U+007E). Contoh berikut mencoba menemukan indeks karakter LATIN CAPITAL LETTER A WITH GRAVE (U+00C0) dalam string. Namun, karakter ini dapat diwakili dengan dua cara berbeda: sebagai unit kode tunggal (U+00C0) atau sebagai karakter komposit (dua unit kode: U+0041 dan U+0300). Dalam hal ini, karakter diwakili dalam instans string oleh dua Char objek, U+0041 dan U+0300. Contoh kode memanggil String.IndexOf(Char) dan String.IndexOf(String) kelebihan beban untuk menemukan posisi karakter ini dalam instans string, tetapi ini mengembalikan hasil yang berbeda. Panggilan metode pertama memiliki Char argumen; melakukan perbandingan ordinal dan karenanya tidak dapat menemukan kecocokan. Panggilan kedua memiliki String argumen; ia melakukan perbandingan sensitif budaya dan karenanya menemukan kecocokan.

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:   {0}", composite.IndexOf('\u00C0'));
        Console.WriteLine("Comparing using String: {0}", 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

Anda dapat menghindari beberapa ambiguitas contoh ini (panggilan ke dua kelebihan beban serupa dari metode yang mengembalikan hasil yang berbeda) dengan memanggil kelebihan beban yang mencakup StringComparison parameter, seperti String.IndexOf(String, StringComparison) metode atau String.LastIndexOf(String, StringComparison) .

Namun, pencarian tidak selalu peka terhadap budaya. Jika tujuan pencarian adalah untuk membuat keputusan keamanan atau untuk mengizinkan atau melarang akses ke beberapa sumber daya, perbandingannya harus ordinal, seperti yang dibahas di bagian berikutnya.

Menguji string untuk kesetaraan

Jika Anda ingin menguji dua string untuk kesetaraan daripada menentukan bagaimana perbandingannya dalam urutan pengurutan, gunakan String.Equals metode alih-alih metode perbandingan string seperti String.Compare atau CompareInfo.Compare.

Perbandingan untuk kesetaraan biasanya dilakukan untuk mengakses beberapa sumber daya secara kondisional. Misalnya, Anda mungkin melakukan perbandingan untuk kesetaraan untuk memverifikasi kata sandi atau untuk mengonfirmasi bahwa file ada. Perbandingan nonbahasa seperti itu harus selalu ordinal daripada peka terhadap budaya. Secara umum, Anda harus memanggil metode instans String.Equals(String, StringComparison) atau metode statis String.Equals(String, String, StringComparison) dengan nilai StringComparison.Ordinal untuk string seperti kata sandi, dan nilai StringComparison.OrdinalIgnoreCase untuk string seperti nama file atau URI.

Perbandingan untuk kesetaraan terkadang melibatkan pencarian atau perbandingan substring daripada panggilan ke String.Equals metode . Dalam beberapa kasus, Anda dapat menggunakan pencarian substring untuk menentukan apakah substring tersebut sama dengan string lain. Jika tujuan perbandingan ini non-linguistik, pencarian juga harus ordinal daripada peka terhadap budaya.

Contoh berikut menggambarkan bahaya pencarian sensitif budaya pada data non-linguistik. Metode AccessesFileSystem ini dirancang untuk melarang akses sistem file untuk URI yang dimulai dengan substring "FILE". Untuk melakukan ini, ia melakukan perbandingan yang sensitif terhadap budaya dan tidak peka huruf besar/kecil dari awal URI dengan string "FILE". Karena URI yang mengakses sistem file dapat dimulai dengan "FILE:" atau "file:", asumsi implisitnya adalah "i" (U+0069) selalu setara dengan huruf kecil "I" (U+0049). Namun, dalam bahasa Turki dan Azerbaijan, versi huruf besar "i" adalah "İ" (U+0130). Karena perbedaan ini, perbandingan sensitif budaya memungkinkan akses sistem file ketika harus dilarang.

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.

Anda dapat menghindari masalah ini dengan melakukan perbandingan ordinal yang mengabaikan kasus, seperti yang ditunjukkan contoh berikut.

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.

Mengurutkan dan mengurutkan string

Biasanya, string berurutan yang akan ditampilkan di antarmuka pengguna harus diurutkan berdasarkan budaya. Sebagian besar, perbandingan string tersebut ditangani secara implisit oleh .NET saat Anda memanggil metode yang mengurutkan string, seperti Array.Sort atau List<T>.Sort. Secara default, string diurutkan dengan menggunakan konvensi pengurutan budaya saat ini. Contoh berikut menggambarkan perbedaan ketika array string diurutkan dengan menggunakan konvensi budaya Inggris (Amerika Serikat) dan budaya Swedia (Swedia).

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

Perbandingan string yang sensitif terhadap budaya didefinisikan oleh CompareInfo objek, yang dikembalikan oleh properti setiap budaya CultureInfo.CompareInfo . Perbandingan string sensitif budaya yang menggunakan String.Compare metode kelebihan beban juga menggunakan CompareInfo objek .

.NET menggunakan tabel untuk melakukan pengurutan sensitif budaya pada data string. Konten tabel ini, yang berisi data pada bobot pengurutan dan normalisasi string, ditentukan oleh versi standar Unicode yang diterapkan oleh versi .NET tertentu. Tabel berikut mencantumkan versi Unicode yang diimplementasikan oleh versi .NET yang ditentukan. Daftar versi Unicode yang didukung ini hanya berlaku untuk perbandingan karakter dan pengurutan; tidak berlaku untuk klasifikasi karakter Unicode menurut kategori. Untuk informasi selengkapnya, lihat bagian "String dan Standar Unicode" di String artikel.

Versi .NET Framework Sistem operasi Versi Unicode
.NET Framework 2.0 Semua sistem operasi Unicode 4.1
.NET Framework 3.0 Semua sistem operasi Unicode 4.1
.NET Framework 3,5 Semua sistem operasi Unicode 4.1
.NET Framework 4 Semua sistem operasi Unicode 5.0
.NET Framework 4.5 dan yang lebih baru Windows 7 Unicode 5.0
.NET Framework 4.5 dan yang lebih baru Sistem operasi Windows 8 dan yang lebih baru Unicode 6.3.0
.NET Core dan .NET 5+ Tergantung pada versi Standar Unicode yang didukung oleh OS yang mendasar.

Dimulai dengan .NET Framework 4.5 dan di semua versi .NET Core dan .NET 5+, perbandingan dan pengurutan string tergantung pada sistem operasi. .NET Framework 4.5 dan yang lebih baru yang berjalan pada Windows 7 mengambil data dari tabelnya sendiri yang mengimplementasikan Unicode 5.0. .NET Framework 4.5 dan yang lebih baru berjalan pada Windows 8 dan yang lebih baru mengambil data dari tabel sistem operasi yang mengimplementasikan Unicode 6.3. Pada .NET Core dan .NET 5+, versi Unicode yang didukung tergantung pada sistem operasi yang mendasar. Jika Anda menserialisasikan data yang diurutkan sensitif terhadap budaya, Anda dapat menggunakan SortVersion kelas untuk menentukan kapan data serial Anda perlu diurutkan sehingga konsisten dengan .NET dan urutan sortir sistem operasi. Misalnya, lihat SortVersion topik kelas.

Jika aplikasi Anda melakukan jenis data string khusus budaya yang luas, Anda dapat bekerja dengan SortKey kelas untuk membandingkan string. Kunci pengurutan mencerminkan bobot sortir khusus budaya, termasuk bobot alfabet, huruf besar/kecil, dan diakritik dari string tertentu. Karena perbandingan menggunakan kunci pengurutan adalah biner, mereka lebih cepat daripada perbandingan yang menggunakan CompareInfo objek baik secara implisit maupun eksplisit. Anda membuat kunci pengurutan khusus budaya untuk string tertentu dengan meneruskan string ke CompareInfo.GetSortKey metode .

Contoh berikut mirip dengan contoh sebelumnya. Namun, alih-alih memanggil Array.Sort(Array) metode , yang secara implisit CompareInfo.Compare memanggil metode , itu mendefinisikan System.Collections.Generic.IComparer<T> implementasi yang membandingkan kunci pengurutan, yang membuat instans dan meneruskan ke Array.Sort<T>(T[], IComparer<T>) metode .

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

Hindari perangkaian string

Jika memungkinkan, hindari menggunakan string komposit yang dibangun pada waktu proses dari frasa yang digabungkan. String komposit sulit dilokalkan, karena seringkali mengasumsikan urutan tata bahasa dalam bahasa asli aplikasi yang tidak berlaku untuk bahasa lokal lainnya.

Menangani tanggal dan waktu

Cara Anda menangani nilai tanggal dan waktu bergantung pada apakah nilai tersebut ditampilkan di antarmuka pengguna atau tetap ada. Bagian ini memeriksa kedua penggunaan. Ini juga membahas bagaimana Anda dapat menangani perbedaan zona waktu dan operasi aritmatika saat bekerja dengan tanggal dan waktu.

Tampilkan tanggal dan waktu

Biasanya, ketika tanggal dan waktu ditampilkan di antarmuka pengguna, Anda harus menggunakan konvensi pemformatan budaya pengguna, yang ditentukan oleh CultureInfo.CurrentCulture properti dan oleh DateTimeFormatInfo objek yang dikembalikan oleh CultureInfo.CurrentCulture.DateTimeFormat properti . Konvensi pemformatan budaya saat ini secara otomatis digunakan saat Anda memformat tanggal dengan menggunakan salah satu metode ini:

Contoh berikut menampilkan data matahari terbit dan terbenam dua kali untuk 11 Oktober 2012. Pertama-tama menetapkan budaya saat ini ke Kroasia (Kroasia), dan kemudian ke Inggris (Inggris). Dalam setiap kasus, tanggal dan waktu ditampilkan dalam format yang sesuai untuk budaya tersebut.

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: {0:D}", dates[0]);
        Console.WriteLine("   Sunrise: {0:T}", dates[0]);
        Console.WriteLine("   Sunset:  {0:T}", dates[1]);
    }
}

// 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

Pertahankan tanggal dan waktu

Anda tidak boleh mempertahankan data tanggal dan waktu dalam format yang dapat bervariasi menurut budaya. Ini adalah kesalahan pemrograman umum yang menghasilkan data yang rusak atau pengecualian run-time. Contoh berikut menserialisasikan dua tanggal, 9 Januari 2013 dan 18 Agustus 2013, sebagai string dengan menggunakan konvensi pemformatan budaya Inggris (Amerika Serikat). Ketika data diambil dan diurai dengan menggunakan konvensi budaya Bahasa Inggris (Amerika Serikat), data berhasil dipulihkan. Namun, ketika diambil dan diurai dengan menggunakan konvensi budaya Inggris (Inggris), tanggal pertama salah ditafsirkan sebagai 1 September, dan yang kedua gagal diurai karena kalender Gregorian tidak memiliki bulan kedelapan belas.

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: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName);
        foreach (var dateStr in dateStrings)
        {
            DateTime restoredDate;
            if (DateTime.TryParse(dateStr, out restoredDate))
                Console.WriteLine("The date is {0:D}", restoredDate);
            else
                Console.WriteLine("ERROR: Unable to parse {0}", 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: {0}",
                          Thread.CurrentThread.CurrentCulture.DisplayName);
        foreach (var dateStr in dateStrings)
        {
            DateTime restoredDate;
            if (DateTime.TryParse(dateStr, out restoredDate))
                Console.WriteLine("The date is {0:D}", restoredDate);
            else
                Console.WriteLine("ERROR: Unable to parse {0}", 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

Anda dapat menghindari masalah ini dengan salah satu dari tiga cara:

  • Serialisasi tanggal dan waktu dalam format biner daripada sebagai string.
  • Simpan dan uraikan representasi string tanggal dan waktu dengan menggunakan string format kustom yang sama terlepas dari budaya pengguna.
  • Simpan string dengan menggunakan konvensi pemformatan budaya invarian.

Contoh berikut mengilustrasikan pendekatan terakhir. Ini menggunakan konvensi pemformatan budaya invarian yang dikembalikan oleh properti statis 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: {0}",
                          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 {0:D}", restoredDate);
            else
                Console.WriteLine("ERROR: Unable to parse {0}", 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: {0}",
                          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 {0:D}", restoredDate);
            else
                Console.WriteLine("ERROR: Unable to parse {0}", 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

Serialisasi dan kesadaran zona waktu

Nilai tanggal dan waktu dapat memiliki beberapa interpretasi, mulai dari waktu umum ("Toko buka pada 2 Januari 2013, pukul 09:00 A.M.") hingga waktu tertentu ("Tanggal lahir: 2 Januari 2013 6:32:00 A.M."). Ketika nilai waktu mewakili momen tertentu dalam waktu dan Anda memulihkannya dari nilai berseri, Anda harus memastikan bahwa nilai tersebut mewakili momen yang sama dalam waktu terlepas dari lokasi geografis atau zona waktu pengguna.

Contoh berikut mengilustrasikan masalah ini. Ini menyimpan satu nilai tanggal dan waktu lokal sebagai string dalam tiga format standar:

  • "G" untuk tanggal umum lama.
  • "s" untuk tanggal/waktu yang dapat diurutkan.
  • "o" untuk tanggal/waktu pulang pergi.
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("'{0}' --> {1} {2}",
                              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

Ketika data dipulihkan pada sistem di zona waktu yang sama dengan sistem tempat data diserialisasikan, nilai tanggal dan waktu yang dideserialisasi secara akurat mencerminkan nilai asli, seperti yang ditunjukkan oleh output:

'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

Namun, jika Anda memulihkan data pada sistem di zona waktu yang berbeda, hanya nilai tanggal dan waktu yang diformat dengan string format standar "o" (pulang pergi) yang mempertahankan informasi zona waktu dan oleh karena itu mewakili instans waktu yang sama. Berikut adalah output ketika data tanggal dan waktu dipulihkan pada sistem di zona Waktu Standar Romansa:

'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

Untuk secara akurat mencerminkan nilai tanggal dan waktu yang mewakili satu momen waktu terlepas dari zona waktu sistem tempat data dideserialisasi, Anda dapat melakukan salah satu hal berikut:

  • Simpan nilai sebagai string dengan menggunakan string format standar "o" (pulang pergi). Kemudian deserialisasi pada sistem target.
  • Konversikan ke UTC dan simpan sebagai string dengan menggunakan string format standar "r" (RFC1123). Kemudian deserialisasi pada sistem target dan konversikan ke waktu lokal.
  • Konversikan ke UTC dan simpan sebagai string dengan menggunakan string format standar "u" (universal sortable). Kemudian deserialisasi pada sistem target dan konversikan ke waktu lokal.

Contoh berikut mengilustrasikan setiap teknik.

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

Ketika data diserialisasikan pada sistem di zona Waktu Standar Pasifik dan dideserialisasi pada sistem di zona Waktu Standar Romansa, contoh menampilkan output berikut:

'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

Untuk informasi selengkapnya, lihat Mengonversi waktu antar zona waktu.

Melakukan aritmatika tanggal dan waktu

DateTime Kedua jenis dan DateTimeOffset mendukung operasi aritmatika. Anda dapat menghitung perbedaan antara dua nilai tanggal, atau Anda dapat menambahkan atau mengurangi interval waktu tertentu ke atau dari nilai tanggal. Namun, operasi aritmatika pada nilai tanggal dan waktu tidak memperhitungkan zona waktu dan aturan penyesuaian zona waktu. Karena itu, aritmatika tanggal dan waktu pada nilai yang mewakili momen dalam waktu dapat mengembalikan hasil yang tidak akurat.

Misalnya, transisi dari Waktu Standar Pasifik ke Waktu Siang Pasifik terjadi pada Minggu kedua Maret, yaitu 10 Maret untuk tahun 2013. Seperti yang ditunjukkan contoh berikut, jika Anda menghitung tanggal dan waktu yaitu 48 jam setelah 9 Maret 2013 pukul 10:30 PAGI pada sistem di zona Waktu Standar Pasifik, hasilnya, 11 Maret 2013 pukul 10:30 A.M., tidak memperhitungkan penyesuaian waktu intervensi.

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("{0:g} + {1:N1} hours = {2:g}",
                          date1, interval.TotalHours, date2);
    }
}

// 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

Untuk memastikan bahwa operasi aritmatika pada nilai tanggal dan waktu menghasilkan hasil yang akurat, ikuti langkah-langkah berikut:

  1. Konversikan waktu di zona waktu sumber ke UTC.
  2. Lakukan operasi aritmatika.
  3. Jika hasilnya adalah nilai tanggal dan waktu, konversikan dari UTC ke waktu di zona waktu sumber.

Contoh berikut mirip dengan contoh sebelumnya, kecuali mengikuti tiga langkah ini untuk menambahkan 48 jam dengan benar ke 9 Maret 2013 pukul 10:30 A.M.

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("{0:g} + {1:N1} hours = {2:g}",
                          date1, interval.TotalHours, date2);
    }
}

// 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

Untuk informasi selengkapnya, lihat Melakukan operasi aritmatika dengan tanggal dan waktu.

Menggunakan nama sensitif budaya untuk elemen tanggal

Aplikasi Anda mungkin perlu menampilkan nama bulan atau hari dalam seminggu. Untuk melakukan ini, kode seperti berikut ini adalah umum.

using System;

public class Example12
{
   public static void Main12()
   {
      DateTime midYear = new DateTime(2013, 7, 1);
      Console.WriteLine("{0:d} is a {1}.", midYear, 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.

Namun, kode ini selalu mengembalikan nama hari dalam seminggu dalam bahasa Inggris. Kode yang mengekstrak nama bulan seringkali bahkan lebih tidak fleksibel. Ini sering mengasumsikan kalender dua belas bulan dengan nama bulan dalam bahasa tertentu.

Dengan menggunakan string format tanggal dan waktu kustom atau properti DateTimeFormatInfo objek, mudah untuk mengekstrak string yang mencerminkan nama hari dalam seminggu atau bulan dalam budaya pengguna, seperti yang diilustrasikan contoh berikut. Ini mengubah budaya saat ini menjadi Bahasa Prancis (Prancis) dan menampilkan nama hari dalam seminggu dan nama bulan untuk 1 Juli 2013.

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("{0:d} is a {1}.", midYear, DateUtilities.GetDayName(midYear));
        Console.WriteLine("{0:d} is a {1}.", midYear, DateUtilities.GetDayName((int)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));
    }
}

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.

Nilai numerik

Penanganan angka tergantung pada apakah angka ditampilkan di antarmuka pengguna atau tetap ada. Bagian ini memeriksa kedua penggunaan.

Catatan

Dalam operasi penguraian dan pemformatan, .NET hanya mengenali karakter Latin Dasar 0 hingga 9 (U+0030 hingga U+0039) sebagai digit numerik.

Menampilkan nilai numerik

Biasanya, ketika angka ditampilkan di antarmuka pengguna, Anda harus menggunakan konvensi pemformatan budaya pengguna, yang ditentukan oleh CultureInfo.CurrentCulture properti dan oleh objek yang NumberFormatInfo dikembalikan oleh CultureInfo.CurrentCulture.NumberFormat properti . Konvensi pemformatan budaya saat ini secara otomatis digunakan saat Anda memformat tanggal dengan cara berikut:

  • Menggunakan metode tanpa ToString parameter dari jenis numerik apa pun.
  • ToString(String) Menggunakan metode jenis numerik apa pun, yang mencakup string format sebagai argumen.
  • Menggunakan pemformatan komposit dengan nilai numerik.

Contoh berikut menampilkan suhu rata-rata per bulan di Paris, Prancis. Ini pertama-tama mengatur budaya saat ini ke Prancis (Prancis) sebelum menampilkan data, dan kemudian mengaturnya ke bahasa Inggris (Amerika Serikat). Dalam setiap kasus, nama dan suhu bulan ditampilkan dalam format yang sesuai untuk budaya tersebut. Perhatikan bahwa kedua budaya menggunakan pemisah desimal yang berbeda dalam nilai suhu. Perhatikan juga bahwa contoh menggunakan string format tanggal dan waktu kustom "MMMM" untuk menampilkan nama bulan penuh, dan mengalokasikan jumlah ruang yang sesuai untuk nama bulan dalam string hasil dengan menentukan panjang nama bulan terpanjang dalam DateTimeFormatInfo.MonthNames array.

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: {0}", 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: {0}", 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

Pertahankan nilai numerik

Anda tidak boleh mempertahankan data numerik dalam format khusus budaya. Ini adalah kesalahan pemrograman umum yang menghasilkan data yang rusak atau pengecualian run-time. Contoh berikut menghasilkan sepuluh angka floating-point acak, lalu menserialisasikannya sebagai string dengan menggunakan konvensi pemformatan budaya Bahasa Inggris (Amerika Serikat). Ketika data diambil dan diurai dengan menggunakan konvensi budaya Bahasa Inggris (Amerika Serikat), data berhasil dipulihkan. Namun, ketika diambil dan diurai dengan menggunakan konvensi budaya Prancis (Prancis), tidak ada angka yang dapat diurai karena budaya menggunakan pemisah desimal yang berbeda.

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: {0}",
                          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 '{0}'", 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: {0}",
                          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 '{0}'", 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'

Untuk menghindari masalah ini, Anda dapat menggunakan salah satu teknik berikut:

  • Simpan dan uraikan representasi string angka dengan menggunakan string format kustom yang sama terlepas dari budaya pengguna.
  • Simpan angka sebagai string dengan menggunakan konvensi pemformatan budaya invarian, yang dikembalikan oleh CultureInfo.InvariantCulture properti .

Menserialisasikan nilai mata uang adalah kasus khusus. Karena nilai mata uang tergantung pada unit mata uang yang dinyatakannya, tidak masuk akal untuk memperlakukannya sebagai nilai numerik independen. Namun, jika Anda menyimpan nilai mata uang sebagai string yang diformat yang menyertakan simbol mata uang, nilai tersebut tidak dapat dideserialisasi pada sistem yang budaya defaultnya menggunakan simbol mata uang yang berbeda, seperti yang ditunjukkan contoh berikut.

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: {0}", CultureInfo.CurrentCulture.DisplayName);
      Console.WriteLine("Currency Value: {0:C2}", value);

      // 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 '{0}'", 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: {0}",
                        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 '{0}'", 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'

Sebagai gantinya, Anda harus menserialisasikan nilai numerik bersama dengan beberapa informasi budaya, seperti nama budaya, sehingga nilai dan simbol mata uangnya dapat dideserialisasi secara independen dari budaya saat ini. Contoh berikut melakukan itu dengan menentukan CurrencyValue struktur dengan dua anggota: Decimal nilai dan nama budaya tempat nilai berada.

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

Bekerja dengan pengaturan khusus budaya

Di .NET, CultureInfo kelas mewakili budaya atau wilayah tertentu. Beberapa propertinya mengembalikan objek yang memberikan informasi spesifik tentang beberapa aspek budaya:

Secara umum, jangan membuat asumsi tentang nilai properti tertentu CultureInfo dan objek terkaitnya. Sebagai gantinya, Anda harus melihat data khusus budaya yang dapat berubah, karena alasan ini:

  • Nilai properti individual dapat berubah dan revisi dari waktu ke waktu, karena data diperbaika, data yang lebih baik tersedia, atau konvensi khusus budaya berubah.

  • Nilai properti individual dapat bervariasi di seluruh versi .NET atau versi sistem operasi.

  • .NET mendukung budaya pengganti. Hal ini memungkinkan untuk mendefinisikan budaya kustom baru yang melengkapi budaya standar yang ada atau sepenuhnya menggantikan budaya standar yang ada.

  • Pada sistem Windows, pengguna dapat menyesuaikan pengaturan khusus budaya dengan menggunakan aplikasi Wilayah dan Bahasa di Panel Kontrol. Saat membuat instans CultureInfo objek, Anda dapat menentukan apakah objek tersebut mencerminkan penyesuaian pengguna ini dengan memanggil CultureInfo(String, Boolean) konstruktor. Biasanya, untuk aplikasi pengguna akhir, Anda harus menghormati preferensi pengguna sehingga pengguna disajikan dengan data dalam format yang mereka harapkan.

Lihat juga