Dukungan TanggalWaktu dan DateTimeOffset di System.Text.Json

Pustaka System.Text.Json mengurai dan menulis dan DateTimeOffset nilai DateTime sesuai dengan profil yang diperluas ISO 8601-1:2019. Pengonversi menyediakan dukungan kustom untuk serialisasi dan deserialisasi dengan JsonSerializer. Anda juga dapat menggunakan Utf8JsonReader dan Utf8JsonWriter untuk menerapkan dukungan kustom.

Dukungan untuk format ISO 8601-1:2019

JsonSerializerRepresentasi , , Utf8JsonReaderUtf8JsonWriter, dan JsonElement ketik uraian dan tulis DateTime dan DateTimeOffset teks sesuai dengan profil yang diperluas dari format ISO 8601-1:2019. Contohnya,2019-07-26T16:59:57-05:00.

Data DateTime dan DateTimeOffset dapat diberi seri dengan JsonSerializer:

using System.Text.Json;

public class Example
{
    private class Product
    {
        public string? Name { get; set; }
        public DateTime ExpiryDate { get; set; }
    }

    public static void Main(string[] args)
    {
        Product p = new Product();
        p.Name = "Banana";
        p.ExpiryDate = new DateTime(2019, 7, 26);

        string json = JsonSerializer.Serialize(p);
        Console.WriteLine(json);
    }
}

// The example displays the following output:
// {"Name":"Banana","ExpiryDate":"2019-07-26T00:00:00"}

DateTime dan DateTimeOffset juga dapat dideserialisasi dengan JsonSerializer:

using System.Text.Json;

public class Example
{
    private class Product
    {
        public string? Name { get; set; }
        public DateTime ExpiryDate { get; set; }
    }

    public static void Main(string[] args)
    {
        string json = @"{""Name"":""Banana"",""ExpiryDate"":""2019-07-26T00:00:00""}";
        Product p = JsonSerializer.Deserialize<Product>(json)!;
        Console.WriteLine(p.Name);
        Console.WriteLine(p.ExpiryDate);
    }
}

// The example displays output similar to the following:
// Banana
// 7/26/2019 12:00:00 AM

Dengan opsi default, input representasi teks DateTime dan DateTimeOffset harus sesuai dengan profil ISO 8601-1:2019 yang diperluas. Mencoba mendeserialisasi representasi yang tidak sesuai dengan profil akan menyebabkan JsonSerializer melemparkan JsonException:

using System.Text.Json;

public class Example
{
    private class Product
    {
        public string? Name { get; set; }
        public DateTime ExpiryDate { get; set; }
    }

    public static void Main(string[] args)
    {
        string json = @"{""Name"":""Banana"",""ExpiryDate"":""26/07/2019""}";
        try
        {
            Product _ = JsonSerializer.Deserialize<Product>(json)!;
        }
        catch (JsonException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}

// The example displays the following output:
// The JSON value could not be converted to System.DateTime. Path: $.ExpiryDate | LineNumber: 0 | BytePositionInLine: 42.

JsonDocument menyediakan akses terstruktur ke konten payload JSON, termasuk representasi DateTime dan DateTimeOffset. Contoh berikut menunjukkan cara menghitung suhu rata-rata pada hari Senin dari kumpulan suhu:

using System.Text.Json;

public class Example
{
    private static double ComputeAverageTemperatures(string json)
    {
        JsonDocumentOptions options = new JsonDocumentOptions
        {
            AllowTrailingCommas = true
        };

        using (JsonDocument document = JsonDocument.Parse(json, options))
        {
            int sumOfAllTemperatures = 0;
            int count = 0;

            foreach (JsonElement element in document.RootElement.EnumerateArray())
            {
                DateTimeOffset date = element.GetProperty("date").GetDateTimeOffset();

                if (date.DayOfWeek == DayOfWeek.Monday)
                {
                    int temp = element.GetProperty("temp").GetInt32();
                    sumOfAllTemperatures += temp;
                    count++;
                }
            }

            double averageTemp = (double)sumOfAllTemperatures / count;
            return averageTemp;
        }
    }

    public static void Main(string[] args)
    {
        string json =
                @"[" +
                    @"{" +
                        @"""date"": ""2013-01-07T00:00:00Z""," +
                        @"""temp"": 23," +
                    @"}," +
                    @"{" +
                        @"""date"": ""2013-01-08T00:00:00Z""," +
                        @"""temp"": 28," +
                    @"}," +
                    @"{" +
                        @"""date"": ""2013-01-14T00:00:00Z""," +
                        @"""temp"": 8," +
                    @"}," +
                @"]";

        Console.WriteLine(ComputeAverageTemperatures(json));
    }
}

// The example displays the following output:
// 15.5

Mencoba menghitung suhu rata-rata yang diberikan payload dengan representasi DateTime yang tidak sesuai akan menyebabkan JsonDocument melemparkan FormatException:

using System.Text.Json;

public class Example
{
    private static double ComputeAverageTemperatures(string json)
    {
        JsonDocumentOptions options = new JsonDocumentOptions
        {
            AllowTrailingCommas = true
        };

        using (JsonDocument document = JsonDocument.Parse(json, options))
        {
            int sumOfAllTemperatures = 0;
            int count = 0;

            foreach (JsonElement element in document.RootElement.EnumerateArray())
            {
                DateTimeOffset date = element.GetProperty("date").GetDateTimeOffset();

                if (date.DayOfWeek == DayOfWeek.Monday)
                {
                    int temp = element.GetProperty("temp").GetInt32();
                    sumOfAllTemperatures += temp;
                    count++;
                }
            }

            double averageTemp = (double)sumOfAllTemperatures / count;
            return averageTemp;
        }
    }

    public static void Main(string[] args)
    {
        // Computing the average temperatures will fail because the DateTimeOffset
        // values in the payload do not conform to the extended ISO 8601-1:2019 profile.
        string json =
                @"[" +
                    @"{" +
                        @"""date"": ""2013/01/07 00:00:00Z""," +
                        @"""temp"": 23," +
                    @"}," +
                    @"{" +
                        @"""date"": ""2013/01/08 00:00:00Z""," +
                        @"""temp"": 28," +
                    @"}," +
                    @"{" +
                        @"""date"": ""2013/01/14 00:00:00Z""," +
                        @"""temp"": 8," +
                    @"}," +
                @"]";

        Console.WriteLine(ComputeAverageTemperatures(json));
    }
}

// The example displays the following output:
// Unhandled exception.System.FormatException: One of the identified items was in an invalid format.
//    at System.Text.Json.JsonElement.GetDateTimeOffset()

Tingkat Utf8JsonWriter yang lebih bawah menulis data DateTime dan DateTimeOffset:

using System.Text;
using System.Text.Json;

public class Example
{
    public static void Main(string[] args)
    {
        JsonWriterOptions options = new JsonWriterOptions
        {
            Indented = true
        };

        using (MemoryStream stream = new MemoryStream())
        {
            using (Utf8JsonWriter writer = new Utf8JsonWriter(stream, options))
            {
                writer.WriteStartObject();
                writer.WriteString("date", DateTimeOffset.UtcNow);
                writer.WriteNumber("temp", 42);
                writer.WriteEndObject();
            }

            string json = Encoding.UTF8.GetString(stream.ToArray());
            Console.WriteLine(json);
        }
    }
}

// The example output similar to the following:
// {
//     "date": "2019-07-26T00:00:00+00:00",
//     "temp": 42
// }

Utf8JsonReader mengurai data DateTime dan DateTimeOffset:

using System.Text;
using System.Text.Json;

public class Example
{
    public static void Main(string[] args)
    {
        byte[] utf8Data = Encoding.UTF8.GetBytes(@"""2019-07-26T00:00:00""");

        Utf8JsonReader json = new Utf8JsonReader(utf8Data);
        while (json.Read())
        {
            if (json.TokenType == JsonTokenType.String)
            {
                Console.WriteLine(json.TryGetDateTime(out DateTime datetime));
                Console.WriteLine(datetime);
                Console.WriteLine(json.GetDateTime());
            }
        }
    }
}

// The example displays output similar to the following:
// True
// 7/26/2019 12:00:00 AM
// 7/26/2019 12:00:00 AM

Mencoba membaca format yang tidak sesuai dengan Utf8JsonReader akan menyebabkannya melempar FormatException:

using System.Text;
using System.Text.Json;

public class Example
{
    public static void Main(string[] args)
    {
        byte[] utf8Data = Encoding.UTF8.GetBytes(@"""2019/07/26 00:00:00""");

        Utf8JsonReader json = new Utf8JsonReader(utf8Data);
        while (json.Read())
        {
            if (json.TokenType == JsonTokenType.String)
            {
                Console.WriteLine(json.TryGetDateTime(out DateTime datetime));
                Console.WriteLine(datetime);

                DateTime _ = json.GetDateTime();
            }
        }
    }
}

// The example displays the following output:
// False
// 1/1/0001 12:00:00 AM
// Unhandled exception. System.FormatException: The JSON value is not in a supported DateTime format.
//     at System.Text.Json.Utf8JsonReader.GetDateTime()

Serialisasi properti DateOnly dan TimeOnly

Dengan .NET 7+, System.Text.Json mendukung serialisasi dan deserialisasi DateOnly dan TimeOnly jenis. Pertimbangkan objek berikut:

sealed file record Appointment(
    Guid Id,
    string Description,
    DateOnly Date,
    TimeOnly StartTime,
    TimeOnly EndTime);

Contoh berikut menserialisasikan Appointment objek, menampilkan JSON yang dihasilkan, lalu mendeserialisasinya kembali ke instans baru jenisnya Appointment . Terakhir, instans asli dan yang baru dideserialisasi dibandingkan dengan kesetaraan dan hasilnya ditulis ke konsol:

Appointment originalAppointment = new(
    Id: Guid.NewGuid(),
    Description: "Take dog to veterinarian.",
    Date: new DateOnly(2002, 1, 13),
    StartTime: new TimeOnly(5,15),
    EndTime: new TimeOnly(5, 45));
string serialized = JsonSerializer.Serialize(originalAppointment);

Console.WriteLine($"Resulting JSON: {serialized}");

Appointment deserializedAppointment =
    JsonSerializer.Deserialize<Appointment>(serialized)!;

bool valuesAreTheSame = originalAppointment == deserializedAppointment;
Console.WriteLine($"""
    Original record has the same values as the deserialized record: {valuesAreTheSame}
    """);

Dalam kode sebelumnya:

  • Objek Appointment dibuat dan ditetapkan ke appointment variabel.
  • appointment Instans diserialisasikan ke JSON menggunakan JsonSerializer.Serialize.
  • JSON yang dihasilkan ditulis ke konsol.
  • JSON dideserialisasi kembali ke instans baru jenis Appointment menggunakan JsonSerializer.Deserialize.
  • Instans asli dan yang baru dideserialisasi dibandingkan dengan kesetaraan.
  • Hasil perbandingan ditulis ke konsol.

Dukungan kustom untuk DateTime dan DateTimeOffset

Saat menggunakan JsonSerializer

Jika ingin serializer melakukan penguraian atau pemformatan kustom, Anda dapat menerapkan pengonversi kustom. Berikut beberapa contohnya:

DateTime(Offset). Uraikan dan DateTime(Offset). ToString

Jika tidak dapat menentukan format input representasi teks DateTime atau DateTimeOffset, Anda dapat menggunakan metode DateTime(Offset).Parse dalam logika baca pengonversi Anda. Metode ini memungkinkan Anda untuk menggunakan . Dukungan ekstensif NET untuk mengurai berbagai DateTime format teks dan DateTimeOffset , termasuk string non-ISO 8601 dan format ISO 8601 yang tidak sesuai dengan profil ISO 8601-1:2019 yang diperluas. Pendekatan ini kurang berkinerja daripada menggunakan implementasi asli serializer.

Untuk serialisasi, Anda dapat menggunakan metode DateTime(Offset).ToString dalam logika penulisan konverter Anda. Metode ini memungkinkan Anda menulis DateTime dan DateTimeOffset nilai menggunakan salah satu format tanggal dan waktu standar, serta format tanggal dan waktu kustom. Pendekatan ini juga kurang berkinerja daripada menggunakan implementasi asli serializer.

using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace DateTimeConverterExamples;

public class DateTimeConverterUsingDateTimeParse : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Debug.Assert(typeToConvert == typeof(DateTime));
        return DateTime.Parse(reader.GetString() ?? string.Empty);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString());
    }
}

class Program
{
    private static void ParseDateTimeWithDefaultOptions()
    {
        DateTime _ = JsonSerializer.Deserialize<DateTime>(@"""04-10-2008 6:30 AM""");
    }

    private static void FormatDateTimeWithDefaultOptions()
    {
        Console.WriteLine(JsonSerializer.Serialize(DateTime.Parse("04-10-2008 6:30 AM -4")));
    }

    private static void ProcessDateTimeWithCustomConverter()
    {
        JsonSerializerOptions options = new JsonSerializerOptions();
        options.Converters.Add(new DateTimeConverterUsingDateTimeParse());

        string testDateTimeStr = "04-10-2008 6:30 AM";
        string testDateTimeJson = @"""" + testDateTimeStr + @"""";

        DateTime resultDateTime = JsonSerializer.Deserialize<DateTime>(testDateTimeJson, options);
        Console.WriteLine(resultDateTime);

        string resultDateTimeJson = JsonSerializer.Serialize(DateTime.Parse(testDateTimeStr), options);
        Console.WriteLine(Regex.Unescape(resultDateTimeJson));
    }

    static void Main(string[] args)
    {
        // Parsing non-compliant format as DateTime fails by default.
        try
        {
            ParseDateTimeWithDefaultOptions();
        }
        catch (JsonException e)
        {
            Console.WriteLine(e.Message);
        }

        // Formatting with default options prints according to extended ISO 8601 profile.
        FormatDateTimeWithDefaultOptions();

        // Using converters gives you control over the serializers parsing and formatting.
        ProcessDateTimeWithCustomConverter();
    }
}

// The example displays output similar to the following:
// The JSON value could not be converted to System.DateTime. Path: $ | LineNumber: 0 | BytePositionInLine: 20.
// "2008-04-10T06:30:00-04:00"
// 4/10/2008 6:30:00 AM
// "4/10/2008 6:30:00 AM"

Catatan

Saat menerapkan JsonConverter<T>, dan T adalah DateTime, parameter typeToConvert akan selalu typeof(DateTime). Parameter ini berguna untuk menangani kasus polimorfik dan saat menggunakan generik untuk mendapatkan typeof(T) dengan cara yang berkinerja.

Utf8Parser dan Utf8Formatter

Anda dapat menggunakan metode penguraian dan pemformatan berbasis UTF-8 cepat dalam logika pengonversi Anda jika input representasi teks DateTime atau DateTimeOffset sesuai dengan salah satu string format tanggal dan waktu standar "R", "l", "O", atau "G", atau Anda ingin menulis sesuai dengan salah satu format ini. Pendekatan ini jauh lebih cepat daripada menggunakan sDateTime(Offset).Parse dan DateTime(Offset).ToString.

Contoh berikut menunjukkan pengonversi kustom yang menserialisasikan dan mendeserialisasi DateTime nilai sesuai dengan format standar "R":

using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace DateTimeConverterExamples;

// This converter reads and writes DateTime values according to the "R" standard format specifier:
// https://learn.microsoft.com/dotnet/standard/base-types/standard-date-and-time-format-strings#the-rfc1123-r-r-format-specifier.
public class DateTimeConverterForCustomStandardFormatR : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Debug.Assert(typeToConvert == typeof(DateTime));

        if (Utf8Parser.TryParse(reader.ValueSpan, out DateTime value, out _, 'R'))
        {
            return value;
        }

        throw new FormatException();
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        // The "R" standard format will always be 29 bytes.
        Span<byte> utf8Date = new byte[29];

        bool result = Utf8Formatter.TryFormat(value, utf8Date, out _, new StandardFormat('R'));
        Debug.Assert(result);

        writer.WriteStringValue(utf8Date);
    }
}

class Program
{
    private static void ParseDateTimeWithDefaultOptions()
    {
        DateTime _ = JsonSerializer.Deserialize<DateTime>(@"""Thu, 25 Jul 2019 13:36:07 GMT""");
    }

    private static void ProcessDateTimeWithCustomConverter()
    {
        JsonSerializerOptions options = new JsonSerializerOptions();
        options.Converters.Add(new DateTimeConverterForCustomStandardFormatR());

        string testDateTimeStr = "Thu, 25 Jul 2019 13:36:07 GMT";
        string testDateTimeJson = @"""" + testDateTimeStr + @"""";

        DateTime resultDateTime = JsonSerializer.Deserialize<DateTime>(testDateTimeJson, options);
        Console.WriteLine(resultDateTime);

        Console.WriteLine(JsonSerializer.Serialize(DateTime.Parse(testDateTimeStr), options));
    }

    static void Main(string[] args)
    {
        // Parsing non-compliant format as DateTime fails by default.
        try
        {
            ParseDateTimeWithDefaultOptions();
        }
        catch (JsonException e)
        {
            Console.WriteLine(e.Message);
        }

        // Using converters gives you control over the serializers parsing and formatting.
        ProcessDateTimeWithCustomConverter();
    }
}

// The example displays output similar to the following:
// The JSON value could not be converted to System.DateTime.Path: $ | LineNumber: 0 | BytePositionInLine: 31.
// 7/25/2019 1:36:07 PM
// "Thu, 25 Jul 2019 09:36:07 GMT"

Catatan

Panjang format standar "R" akan selalu 29 karakter.

Format "l" (huruf kecil "L") tidak didokumenkan dengan string format tanggal dan waktu standar lainnya karena hanya didukung oleh Utf8Parser jenis dan Utf8Formatter . Formatnya adalah RFC 1123 huruf kecil (versi huruf kecil dari format "R"). Misalnya, "thu, 25 jul 2019 06:36:07 gmt".

Gunakan DateTime(Offset). Mengurai sebagai fallback

Jika biasanya mengharapkan input data DateTime atau DateTimeOffset Anda sesuai dengan profil ISO 8601-1:2019 yang diperluas, Anda dapat menggunakan logika penguraian asli serializer. Anda juga dapat menerapkan mekanisme fallback. Contoh berikut menunjukkan bahwa, setelah gagal mengurai DateTime representasi teks menggunakan TryGetDateTime(DateTime), pengonversi berhasil mengurai data menggunakan Parse(String):

using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace DateTimeConverterExamples;

public class DateTimeConverterUsingDateTimeParseAsFallback : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Debug.Assert(typeToConvert == typeof(DateTime));

        if (!reader.TryGetDateTime(out DateTime value))
        {
            value = DateTime.Parse(reader.GetString()!);
        }

        return value;
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString("dd/MM/yyyy"));
    }
}

class Program
{
    private static void ParseDateTimeWithDefaultOptions()
    {
        DateTime _ = JsonSerializer.Deserialize<DateTime>(@"""2019-07-16 16:45:27.4937872+00:00""");
    }

    private static void ProcessDateTimeWithCustomConverter()
    {
        JsonSerializerOptions options = new JsonSerializerOptions();
        options.Converters.Add(new DateTimeConverterUsingDateTimeParseAsFallback());

        string testDateTimeStr = "2019-07-16 16:45:27.4937872+00:00";
        string testDateTimeJson = @"""" + testDateTimeStr + @"""";

        DateTime resultDateTime = JsonSerializer.Deserialize<DateTime>(testDateTimeJson, options);
        Console.WriteLine(resultDateTime);

        string resultDateTimeJson = JsonSerializer.Serialize(DateTime.Parse(testDateTimeStr), options);
        Console.WriteLine(Regex.Unescape(resultDateTimeJson));
    }

    static void Main(string[] args)
    {
        // Parsing non-compliant format as DateTime fails by default.
        try
        {
            ParseDateTimeWithDefaultOptions();
        }
        catch (JsonException e)
        {
            Console.WriteLine(e.Message);
        }

        // Using converters gives you control over the serializers parsing and formatting.
        ProcessDateTimeWithCustomConverter();
    }
}

// The example displays output similar to the following:
// The JSON value could not be converted to System.DateTime.Path: $ | LineNumber: 0 | BytePositionInLine: 35.
// 7/16/2019 4:45:27 PM
// "16/07/2019"

Gunakan format tanggal epoch Unix

Pengonversi berikut menangani format Unix epoch dengan atau tanpa offset zona waktu (nilai seperti /Date(1590863400000-0700)/ atau /Date(1590863400000)/):

sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
    static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
                || !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
                || !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
        {
            throw new JsonException();
        }

        int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
        TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);

        return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
        TimeSpan utcOffset = value.Offset;

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");

        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
        {
            throw new JsonException();
        }

        return s_epoch.AddMilliseconds(unixTime);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
        writer.WriteStringValue(formatted);
    }
}

Saat menggunakan Utf8JsonWriter

Jika ingin menulis perwakilan teks DateTime atau DateTimeOffset kustom dengan Utf8JsonWriter, Anda dapat memformat representasi kustom menjadi String, ReadOnlySpan<Byte>, ReadOnlySpan<Char>, atau JsonEncodedText, lalu meneruskannya ke metode Utf8JsonWriter.WriteStringValue atau Utf8JsonWriter.WriteString yang sesuai.

Contoh berikut menunjukkan bagaimana format kustom DateTime dapat dibuat dengan ToString(String, IFormatProvider) lalu ditulis dengan WriteStringValue(String) metode :

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

public class Example
{
    public static void Main(string[] args)
    {
        var options = new JsonWriterOptions
        {
            Indented = true
        };

        using (var stream = new MemoryStream())
        {
            using (var writer = new Utf8JsonWriter(stream, options))
            {
                string dateStr = DateTime.UtcNow.ToString("F", CultureInfo.InvariantCulture);

                writer.WriteStartObject();
                writer.WriteString("date", dateStr);
                writer.WriteNumber("temp", 42);
                writer.WriteEndObject();
            }

            string json = Encoding.UTF8.GetString(stream.ToArray());
            Console.WriteLine(json);
        }
    }
}

// The example displays output similar to the following:
// {
//     "date": "Tuesday, 27 August 2019 19:21:44",
//     "temp": 42
// }

Saat menggunakan Utf8JsonReader

Jika Anda ingin membaca representasi kustom DateTime atau DateTimeOffset teks dengan Utf8JsonReader, Anda bisa mendapatkan nilai token JSON saat ini sebagai String metode penggunaan GetString() , lalu mengurai nilai menggunakan logika kustom.

Contoh berikut menunjukkan bagaimana representasi teks kustom DateTimeOffset dapat diambil menggunakan GetString() metode , lalu diurai menggunakan ParseExact(String, String, IFormatProvider):

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

public class Example
{
    public static void Main(string[] args)
    {
        byte[] utf8Data = Encoding.UTF8.GetBytes(@"""Friday, 26 July 2019 00:00:00""");

        var json = new Utf8JsonReader(utf8Data);
        while (json.Read())
        {
            if (json.TokenType == JsonTokenType.String)
            {
                string value = json.GetString();
                DateTimeOffset dto = DateTimeOffset.ParseExact(value, "F", CultureInfo.InvariantCulture);
                Console.WriteLine(dto);
            }
        }
    }
}

// The example displays output similar to the following:
// 7/26/2019 12:00:00 AM -04:00

Profil ISO 8601-1:2019 yang diperluas di System.Text.Json

Komponen tanggal dan waktu

Profil ISO 8601-1:2019 yang diperluas yang diterapkan dalam System.Text.Json menentukan komponen berikut untuk representasi tanggal dan waktu. Komponen-komponen ini digunakan untuk menentukan berbagai tingkat granularitas yang didukung saat mengurai dan memformat DateTime dan DateTimeOffset representasi.

Komponen Format Deskripsi
Tahun "tttt" 0001-9999
Month "MM" 01-12
Hari "dd" 01-28, 01-29, 01-30, 01-31 berdasarkan bulan/tahun.
Jam "HH" 00-23
Menit "mm" 00-59
Detik "ss" 00-59
Pecahan kedua "FFFFFFF" Minimal satu digit, maksimum 16 digit.
Offset waktu "K" Baik "Z" atau "('+'/'-')HH':'mm".
Waktu parsial "HH':'mm':'ss[FFFFFFF]" Waktu tanpa informasi offset UTC.
Tanggal lengkap "yyyy'-'MM'-'dd" Tanggal kalender.
Purnawaktu "'Partial time'K" UTC hari atau Waktu lokal hari dengan offset waktu antara waktu lokal dan UTC.
Tanggal waktu "'Full date''T''Full time'" Tanggal dan waktu kalender hari ini, misalnya, 2019-07-26T16:59:57-05:00.

Dukungan untuk penguraian

Tingkat granularitas berikut ditentukan untuk penguraian:

  1. 'Full date'

    1. "yyyy'-'MM'-'dd"
  2. "'Full date''T''Hour'':''Minute'"

    1. "yyyy'-'MM'-'dd'T'HH':'mm"
  3. "'Full date''T''Partial time'"

    1. "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (The Sortable ("s") Format Specifier)
    2. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"
  4. "'Full date''T''Time hour'':''Minute''Time offset'"

    1. "yyyy'-'MM'-'dd'T'HH':'mmZ"
    2. "yyyy'-'MM'-'dd'T'HH':'mm('+'/'-')HH':'mm"
  5. 'Date time'

    1. "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
    2. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ"
    3. "yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm"
    4. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"

    Tingkat granularitas ini sesuai dengan RFC 3339, profil ISO 8601 yang diadopsi secara luas yang digunakan untuk bertukar informasi tanggal dan waktu. Namun, ada beberapa batasan dalam implementasi.System.Text.Json

    • RFC 3339 tidak menentukan jumlah maksimum digit pecahan detik, tetapi menentukan bahwa setidaknya satu digit harus mengikuti periode, jika ada bagian pecahan detik. Implementasi dalam System.Text.Json memungkinkan hingga 16 digit (untuk mendukung interop dengan bahasa dan kerangka kerja pemrograman lainnya), tetapi hanya mengurai tujuh digit pertama. JsonException akan dilemparkan jika ada lebih dari 16 digit detik pecahan saat membaca instans DateTime dan DateTimeOffset.
    • RFC 3339 memungkinkan karakter "T" dan "Z" masing-masing menjadi "t" atau "z", tetapi memungkinkan aplikasi untuk membatasi dukungan hanya untuk varian huruf besar. Implementasi dalam System.Text.Json mengharuskan mereka menjadi "T" dan "Z". JsonException akan dilemparkan jika payload input berisi "t" atau "z" saat membaca instans DateTime dan DateTimeOffset.
    • RFC 3339 menentukan bahwa bagian tanggal dan waktu dipisahkan oleh "T", tetapi memungkinkan aplikasi untuk memisahkannya dengan spasi (" ") sebagai gantinya. System.Text.Json memerlukan bagian tanggal dan waktu untuk dipisahkan dengan "T". JsonException akan dilemparkan jika payload input berisi spasi (" ") saat membaca instans DateTime dan DateTimeOffset.

Jika ada pecahan desimal selama detik, setidaknya harus ada satu digit. 2019-07-26T00:00:00. tidak diperbolehkan. Meski hingga 16 digit pecahan diizinkan, hanya tujuh digit pertama yang akan diurai. Selain itu akan dianggap nol. Misalnya, 2019-07-26T00:00:00.1234567890 akan diurai seolah-olah itu 2019-07-26T00:00:00.1234567adalah . Pendekatan ini mempertahankan kompatibilitas dengan implementasi, yang terbatas pada DateTime resolusi ini.

Detik leap tidak didukung.

Dukungan untuk pemformatan

Tingkat granularitas berikut ditentukan untuk penguraian:

  1. "'Full date''T''Partial time'"

    1. "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (The Sortable ("s") Format Specifier)

      Digunakan untuk memformat DateTime tanpa detik pecahan dan tanpa informasi offset.

    2. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"

      Digunakan untuk memformat DateTime dengan detik pecahan, tetapi tanpa informasi offset.

  2. 'Date time'

    1. "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"

      Digunakan untuk memformat DateTime tanpa detik pecahan, tetapi dengan offset UTC.

    2. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ"

      Digunakan untuk memformat DateTime dengan detik pecahan dan dengan offset UTC.

    3. "yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm"

      Digunakan untuk memformat DateTime atau DateTimeOffset tanpa detik pecahan, tetapi dengan offset lokal.

    4. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"

      Digunakan untuk memformat DateTime atau DateTimeOffset dengan detik pecahan dan dengan offset lokal.

    Tingkat granularitas ini sesuai dengan RFC 3339.

Jika representasi format round-trip dari DateTime atau instans DateTimeOffset memiliki nol di akhir dalam detik pecahannya, maka JsonSerializer dan Utf8JsonWriter akan memformat representasi instans tanpa mengikuti nol. Misalnya, instans DateTime yang representasi format round-trip adalah 2019-04-24T14:50:17.1010000Z, akan diformat sebagai 2019-04-24T14:50:17.101Z oleh JsonSerializer dan Utf8JsonWriter.

Jika representasi format round-trip dari DateTime atau instans DateTimeOffset memiliki nol di akhir dalam detik pecahannya, maka JsonSerializer dan Utf8JsonWriter akan memformat representasi instans tanpa mengikuti nol. Misalnya, instans DateTime yang representasi format round-trip adalah 2019-04-24T14:50:17.0000000+02:00, akan diformat sebagai 2019-04-24T14:50:17+02:00 oleh JsonSerializer dan Utf8JsonWriter.

Memotong nol dalam digit pecahan detik memungkinkan output terkecil yang diperlukan guna mempertahankan informasi tentang round trip untuk ditulis.

Maksimum tujuh digit pecahan detik ditulis. Maksimum ini selaras dengan implementasi, yang terbatas pada DateTime resolusi ini.