共用方式為


System.Text.Json 中的 DateTime 和 DateTimeOffset 支援

System.Text.Json 程式庫會根據 ISO 8601-1:2019 擴充設定檔來剖析和寫入 DateTimeDateTimeOffset 值。 轉換器提供使用 JsonSerializer 序列化和還原序列化的自訂支援。 您也可以使用 Utf8JsonReaderUtf8JsonWriter 來實作自訂支援。

ISO 8601-1:2019 格式的支援

JsonSerializerUtf8JsonReaderUtf8JsonWriterJsonElement 型別會根據 ISO 8601-1:2019 格式的擴充設定檔來剖析和寫入 DateTimeDateTimeOffset 文字標記法。 例如: 2019-07-26T16:59:57-05:00

DateTimeDateTimeOffset 資料可以使用 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"}

DateTimeDateTimeOffset 也可以使用 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

使用預設選項時,輸入 DateTimeDateTimeOffset 文字標記法必須符合擴充 ISO 8601-1:2019 設定檔。 嘗試還原序列化不符合設定檔的標記法會導致 JsonSerializer 擲回 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 提供 JSON 承載內容的結構化存取,包括 DateTimeDateTimeOffset 表示法。 下列範例示範如何根據一組溫度集合,計算出星期一的平均溫度:

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

嘗試計算平均溫度時,如果指定的承載具有不符合規範的 DateTime 標記法,將會導致 JsonDocument 擲回 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()

較低層級的 Utf8JsonWriter 寫入 DateTimeDateTimeOffset 資料:

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 剖析 DateTimeDateTimeOffset 資料:

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

嘗試使用 Utf8JsonReader 讀取不符合規範的格式會導致其擲回 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()

序列化 DateOnly 和 TimeOnly 屬性

使用 .NET 7+,System.Text.Json 支援序列化和還原序列化 DateOnlyTimeOnly 型別。 請考慮下列物件:

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

下列範例會序列化 Appointment 物件、顯示產生的 JSON,然後將其還原序列化回 Appointment 類型的新執行個體。 最後,系統會比較原始和新還原序列化的執行個體是否相等,並將結果寫入主控台:

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}
    """);

在上述程式碼中:

  • Appointment 物件會具現化並指派給 appointment 變數。
  • appointment 執行個體會使用 JsonSerializer.Serialize 序列化為 JSON。
  • 產生的 JSON 會寫入主控台。
  • JSON 會使用 JsonSerializer.Deserialize 還原序列化回 Appointment 類型的新執行個體。
  • 系統會比較原始和新還原序列化的執行個體是否相等。
  • 比較的結果會寫入主控台。

DateTimeDateTimeOffset 的自訂支援

使用 JsonSerializer

如果您想要序列化程式執行自訂剖析或格式化,您可以實作自訂轉換器。 以下是一些範例:

DateTime(Offset).Parse 和 DateTime(Offset).ToString

如果您無法判斷輸入 DateTimeDateTimeOffset 文字標記法的格式,您可以在轉換器讀取邏輯中使用 DateTime(Offset).Parse 方法。 此方法可讓您使用 .NET 廣泛的支援來剖析各種 DateTimeDateTimeOffset 文字格式,包括不符合擴充 ISO 8601-1:2019 設定檔的非 ISO 8601 字串和 ISO 8601 格式。 這個方法的效能比使用序列化程式的原生實作還低。

若要進行序列化,您可以在轉換器寫入邏輯中使用 DateTime(Offset).ToString 方法。 這個方法可讓您使用任何標準日期和時間格式,以及自訂日期和時間格式來撰寫 DateTimeDateTimeOffset 值。 這個方法的效能也比使用序列化程式的原生實作還低。

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"

注意

若實作 JsonConverter<T>,且 TDateTime 時,typeToConvert 參數一律為 typeof(DateTime)。 此參數適用於處理多型案例,以及使用泛型以高效能的方式取得 typeof(T) 的情況。

Utf8ParserUtf8Formatter

如果您的輸入 DateTimeDateTimeOffset 文字表示法符合其中一個 「R」、「l」、「O」或「G」標準日期和時間格式字串,或者您想要根據下列其中一種格式撰寫,則可以在轉換器邏輯中使用基於 UTF-8 的快速剖析和格式化方法。 這種方法比使用 DateTime(Offset).ParseDateTime(Offset).ToString快得多。

下列範例顯示的自訂轉換器會根據「R」標準格式DateTime 值序列化和還原序列化:

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"

注意

「R」標準格式一律為 29 個字元長度。

「l」(小寫「L」) 格式不會記錄其他標準日期和時間格式字串,因為只有 Utf8ParserUtf8Formatter 類型支援該格式。 格式為小寫 RFC 1123 (「R」 格式小寫版本)。 例如,「thu, 25 jul 2019 06:36:07 gmt」。

使用 DateTime(Offset).Parse 作為後援

如果您通常預期輸入 DateTimeDateTimeOffset 資料可符合擴充 ISO 8601-1:2019 設定檔,您可以使用序列化程式的原生剖析邏輯。 您也可以實作後援機制。 下列範例顯示,在使用 TryGetDateTime(DateTime) 剖析 DateTime 文字表示法失敗之後,轉換器使用 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"

使用 Unix epoch 日期格式

下列轉換器會處理具有或不含時區位移的 Unix epoch 格式 (例如 /Date(1590863400000-0700)//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);
    }
}

使用 Utf8JsonWriter

如果您想要使用 Utf8JsonWriter 撰寫自訂 DateTimeDateTimeOffset 文字標記法,您可以將自訂表示法的格式設定為 StringReadOnlySpan<Byte>ReadOnlySpan<Char>JsonEncodedText,然後將其傳遞至對應的 Utf8JsonWriter.WriteStringValueUtf8JsonWriter.WriteString 方法。

下列範例示範如何使用 ToString(String, IFormatProvider) 方法來建立自訂 DateTime 格式,然後使用 WriteStringValue(String) 方法進行撰寫:

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

使用 Utf8JsonReader

如果您想要使用 Utf8JsonReader 讀取自訂 DateTimeDateTimeOffset 文字標記法,您可以使用 GetString() 方法以 String 形式取得目前 JSON 權杖的值,然後使用自訂邏輯剖析值。

下列範例示範如何使用 GetString() 方法來擷取自訂 DateTimeOffset 文字標記法,然後使用 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

System.Text.Json 中的擴充 ISO 8601-1:2019 設定檔

日期和時間元件

System.Text.Json 中實作的擴充 ISO 8601-1:2019 設定檔會定義下列日期和時間表示法的元件。 在剖析和格式化 DateTimeDateTimeOffset 表示法時,這些元件可用來定義各種支援的細微性層級。

元件 格式 描述
Year "yyyy" 0001-9999
"MM" 01-12
Day "dd" 01-28、01-29、01-30、01-31,依月/年而定。
Hour "HH" 00-23
Minute "mm" 00-59
Second "ss" 00-59
第二個部分 "FFFFFFF" 最少一位數,最多 16 位數。
時間位移 "K" 「Z」或「('+'/'-')HH':'mm」。
部分時間 「HH':'mm':'ss[FFFFFFF]」 沒有 UTC 位移資訊的時間。
完整日期 "yyyy'-'MM'-'dd" 行事曆日期。
完整時間 「部分時間’K」 日期的 UTC 或當地時間,以及當地時間與 UTC 之間的時間位移。
日期時間 「'完整日期''T''完整時間'」 行事曆日期和時間,例如 2019-07-26T16:59:57-05:00。

支援剖析

下列資料細微性層級是針對剖析所定義的:

  1. 「完整日期」

    1. "yyyy'-'MM'-'dd"
  2. 「'完整日期''T''小時'':''分鐘'」

    1. "yyyy'-'MM'-'dd'T'HH':'mm"
  3. 「'完整日期''T''部分時間'」

    1. "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (可排序的 ("s") 格式指定名稱)
    2. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"
  4. 「'完整日期''T''時間小時'':''分鐘''時間位移'」

    1. "yyyy'-'MM'-'dd'T'HH':'mmZ"
    2. "yyyy'-'MM'-'dd'T'HH':'mm('+'/'-')HH':'mm"
  5. 「日期時間」

    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"

    此層級的資料細微性符合 RFC 3339,這是廣泛採用的 ISO 8601 設定檔,用於變更日期和時間資訊。 不過,System.Text.Json 實作中有一些限制。

    • RFC 3339 未指定小數秒位數上限,但指定如果小數秒區段存在,則小數點後至少要有一位數。 System.Text.Json 中的實作允許最多 16 位數 (以支援與其他程式設計語言和架構進行 Interop),但只會剖析前七個。 如果讀取 DateTimeDateTimeOffset 執行個體時有超過 16 位小數秒數,將會擲回 JsonException
    • RFC 3339 允許「T」和「Z」字元的「t」或「z」版本,但允許應用程式限制僅支援大寫變體。 System.Text.Json 中的實作需要採用「T」和「Z」格式。 讀取 DateTimeDateTimeOffset 執行個體時,如果輸入承載包含「t」或「z」,將會擲回 JsonException
    • RFC 3339 指定日期和時間區段是以「T」分隔,但可讓應用程式改為以空格分隔 (「 」)。 System.Text.Json 需要以「T」分隔日期和時間區段。 讀取 DateTimeDateTimeOffset 執行個體時,如果輸入承載包含空格「 」,將會擲回 JsonException

如果秒數有小數點數,則至少必須有一位小數。 不允許 2019-07-26T00:00:00.。 雖然最多允許 16 個小數位數,但只會剖析前七個。 任何超過此限制的小數都會視為零。 例如,2019-07-26T00:00:00.1234567890 會視為 2019-07-26T00:00:00.1234567 進行剖析。 此方法會維持與 DateTime 實作的相容性,但僅限於此解決方案。

不支援閏秒。

支援格式化

下列資料細微性層級是針對格式化所定義的:

  1. 「'完整日期''T''部分時間'」

    1. "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (可排序的 ("s") 格式指定名稱)

      用來格式化不含小數秒且不含位移資訊的 DateTime

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

      用來格式化含小數秒但不含位移資訊的 DateTime

  2. 「日期時間」

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

      用來格式化不含小數秒但具有 UTC 位移的 DateTime

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

      用來格式化具有小數秒但不含 UTC 位移的 DateTime

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

      用來格式化不含小數秒但具有本地位移的 DateTimeDateTimeOffset

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

      用來格式化具有小數秒和本地位移的 DateTimeDateTimeOffset

    此細微性層級符合 RFC 3339 規範。

如果 DateTimeDateTimeOffset 執行個體的來回格式表示法在其小數秒內具有尾端零,則 JsonSerializerUtf8JsonWriter 會格式化執行個體的表示法以去除尾端零。 例如,來回格式標記法為 2019-04-24T14:50:17.1010000ZDateTime 執行個體,將會由 JsonSerializerUtf8JsonWriter 格式化為 2019-04-24T14:50:17.101Z

如果 DateTimeDateTimeOffset 執行個體的來回格式表示法小數秒都是零,則 JsonSerializerUtf8JsonWriter 會格式化執行個體的表示法以去除小數秒。 例如,來回格式標記法為 2019-04-24T14:50:17.0000000+02:00DateTime 執行個體,將會由 JsonSerializerUtf8JsonWriter 格式化為 2019-04-24T14:50:17+02:00

截斷小數秒位數中的零,可允許保留要寫入來回資訊所需的最小輸出。

最多可寫入七個小數秒位數。 此最大值與 DateTime 實作一致 (僅限於此解決方案)。