Suporte a DateTime e DateTimeOffset em System.Text.Json
A biblioteca System.Text.Json
analisa e grava os valores DateTime e DateTimeOffset de acordo com o perfil estendido ISO 8601-1:2019.
Os conversores fornecem suporte personalizado para serialização e desserialização com JsonSerializer. Também é possível usar Utf8JsonReader e Utf8JsonWriter para implementar o suporte personalizado.
Suporte para o formato ISO 8601-1:2019
Os tipos JsonSerializer, Utf8JsonReader, Utf8JsonWriter e JsonElement analisam e gravam representações de texto DateTime e DateTimeOffset de acordo com o perfil estendido do formato ISO 8601-1:2019. Por exemplo, 2019-07-26T16:59:57-05:00
.
Os dados DateTime e DateTimeOffset podem ser serializados com 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 e DateTimeOffset também podem ser desserializados com 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
Com as opções padrão, as representações de texto de entrada DateTime e DateTimeOffset devem estar em conformidade com o perfil estendido ISO 8601-1:2019. Tentar desserializar representações que não estão em conformidade com o perfil fará com que JsonSerializer gere um 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.
O JsonDocument fornece acesso estruturado ao conteúdo de uma carga JSON, incluindo representações DateTime e DateTimeOffset. O exemplo a seguir mostra como calcular a temperatura média às segundas-feiras a partir de uma coleção de temperaturas:
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
Tentar calcular a temperatura média dada uma carga com representações não compatíveis de DateTime fará com que JsonDocument gere um 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()
O nível inferior Utf8JsonWriter grava dados DateTime e 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 analisa dados DateTime e 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
A tentativa de ler formatos não compatíveis fará com que Utf8JsonReader gere um 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()
Serializar as propriedades DateOnly e TimeOnly
Com o .NET 7+, o System.Text.Json
dá suporte à serialização e desserialização dos tipos DateOnly e TimeOnly. Considere o seguinte objeto:
sealed file record Appointment(
Guid Id,
string Description,
DateOnly Date,
TimeOnly StartTime,
TimeOnly EndTime);
O exemplo a seguir serializa um objeto Appointment
, exibe o JSON resultante e, em seguida, desserializa-o novamente em uma nova instância do tipo Appointment
. Por fim, as instâncias originais e recém-desserializadas são comparadas quanto à igualdade e os resultados são gravados no console:
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}
""");
No código anterior:
- Um objeto
Appointment
é instanciado e atribuído à variávelappointment
. - A instância
appointment
é serializada para JSON usando JsonSerializer.Serialize. - O JSON resultante é gravado no console.
- O JSON é desserializado novamente em uma nova instância do tipo
Appointment
usando JsonSerializer.Deserialize. - As instâncias originais e recém-desserializadas são comparadas quanto à igualdade.
- O resultado da comparação é gravado no console.
Suporte personalizado para DateTime e DateTimeOffset
Ao usar JsonSerializer
Para que o serializador execute a análise ou a formatação personalizadas, você poderá implementar conversores personalizados. Veja alguns exemplos:
DateTime(Offset).Parse e DateTime(Offset).ToString
Se você não conseguir determinar os formatos de suas representações de entrada de texto DateTime ou DateTimeOffset, poderá usar o método DateTime(Offset).Parse
na lógica de leitura do conversor.
Esse método permite que você use o suporte amplo do .NET para analisar vários formatos de texto DateTime e DateTimeOffset, incluindo cadeias de caracteres fora dos formatos ISO 8601 e ISO 8601 que não estão em conformidade com o perfil ISO 8601-1:2019 estendido.
Essa abordagem tem um desempenho menor do que o uso da implementação nativa do serializador.
Para serializar, você pode usar o método DateTime(Offset).ToString
na lógica de gravação do conversor.
Esse método permite gravar os valores DateTime e DateTimeOffset usando quaisquer formatos de data e hora padrão e os formatos de data e hora personalizados.
Essa abordagem também tem um desempenho significativamente menor do que o uso da implementação nativa do serializador.
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"
Observação
Ao implementar JsonConverter<T>, e T
é DateTime, o parâmetro typeToConvert
sempre será typeof(DateTime)
.
O parâmetro é útil para lidar com casos polimórficos e ao usar genéricos para obter typeof(T)
com alto desempenho.
Utf8Parser e Utf8Formatter
Você pode usar métodos rápidos de análise e formatação baseados em UTF 8 em sua lógica de conversor se suas representações de entrada de texto DateTime ou DateTimeOffset estiverem em conformidade com uma das cadeias de caracteres padrão de formato de data e hora "R", "l", "O" ou "G" ou se desejar gravar de acordo com um desses formatos. Essa abordagem é muito mais rápida do que ao usar DateTime(Offset).Parse
e DateTime(Offset).ToString
.
O exemplo a seguir mostra um conversor personalizado que serializa e desserializa os valores DateTime de acordo com o formato padrão "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"
Observação
O formato padrão "R" sempre terá 29 caracteres.
O formato "l" ("L" minúsculo) não está documentado com as outras cadeias de caracteres de formato de data e hora padrão porque tem suporte apenas pelos tipos Utf8Parser
e Utf8Formatter
. O formato é RFC 1123 em minúsculas (uma versão em minúsculas do formato “R”). Por exemplo, "thu, 25 jul 2019 06:36:07 gmt".
Use DateTime(Offset).Parse como um fallback
Se você geralmente espera que sua entrada de dadosDateTime ou DateTimeOffset esteja em conformidade com o perfil estendido ISO 8601-1:2019, você poderá usar a lógica de análise nativa do serializador. Você também pode implementar um mecanismo de fallback. O exemplo a seguir mostra que, depois de não analisar uma representação de texto DateTime usando TryGetDateTime(DateTime), o conversor analisa com êxito os dados usando 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"
Usar o formato de data de época do Unix
Os conversores a seguir manipulam o formato de época Unix com ou sem um deslocamento de fuso horário (valores como /Date(1590863400000-0700)/
ou /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);
}
}
Ao usar Utf8JsonWriter
Se você quiser gravar uma representação de texto personalizada DateTime ou DateTimeOffset com Utf8JsonWriter, você poderá formatar sua representação personalizada para String, ReadOnlySpan<Byte>
, ReadOnlySpan<Char>
ou JsonEncodedText e, em seguida, passá-la para o método Utf8JsonWriter.WriteStringValue ou Utf8JsonWriter.WriteString correspondente.
O exemplo a seguir mostra como um formato personalizado DateTime pode ser criado com ToString(String, IFormatProvider) e, em seguida, gravado com o método 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
// }
Ao usar Utf8JsonReader
Para ler uma representação de texto personalizada DateTime ou DateTimeOffset com Utf8JsonReader, é possível obter o valor do token JSON atual como um String usando o método GetString() e, em seguida, analisar o valor usando a lógica personalizada.
O exemplo a seguir mostra como uma representação de texto personalizada DateTimeOffset pode ser recuperada usando o método GetString() e, em seguida, analisada usando 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
O perfil ISO 8601-1:2019 estendido em System.Text.Json
Componentes de data e hora
O perfil ISO 8601-1:2019 estendido implementado em System.Text.Json define os seguintes componentes para representações de data e hora. Esses componentes são usados para definir vários níveis de granularidade com suporte ao analisar e formatar as representações DateTime e DateTimeOffset.
Componente | Formatar | Descrição |
---|---|---|
Year | "yyyy" | 0001-9999 |
Mês | "MM" | 01-12 |
Dia | "dd" | 01-28, 01-29, 01-30, 01-31 com base no mês/ano. |
Hora | "HH" | 00-23 |
Minuto | "mm" | 00-59 |
Segundo | "ss" | 00-59 |
Segunda fração | "FFFFFFF" | Mínimo de um dígito, máximo de 16 dígitos. |
Deslocamento de horário | "K" | "Z" ou "('+'/'-')HH':'mm". |
Tempo parcial | "HH':'mm':'ss[FFFFFFF]" | Tempo sem informações de diferença UTC. |
Data completa | "yyyy'-'MM'-'dd" | Data do calendário. |
Tempo completo | "'Partial time'K" | UTC do dia ou hora local do dia com a diferença de tempo entre a hora local e UTC. |
Data e hora | "'Full date''T''Full time'" | Data e hora do calendário, por exemplo, 2019-07-26T16:59:57-05:00. |
Suporte para análise
Os seguintes níveis de granularidade são definidos para análise:
'Data completa'
- "yyyy'-'MM'-'dd"
"'Full date''T''Hour'':''Minute'"
- "yyyy'-'MM'-'dd'T'HH':'mm"
"'Full date''T''Partial time'"
- "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (Especificador de formato ("s") classificável)
- "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"
"'Full date''T''Time hour'':''Minute''Time offset'"
- "yyyy'-'MM'-'dd'T'HH':'mmZ"
- "yyyy'-'MM'-'dd'T'HH':'mm('+'/'-')HH':'mm"
'Date time'
- "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
- "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ"
- "yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm"
- "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"
Esse nível de granularidade está em conformidade com o RFC 3339, um perfil amplamente adotado do ISO 8601 usado para trocar informações de data e hora. No entanto, há algumas restrições na implementação de
System.Text.Json
.- O RFC 3339 não especifica um número máximo de dígitos de segundo fracionário, mas especifica que pelo menos um dígito deve seguir o período, se uma seção fracionária de segundo estiver presente. A implementação em
System.Text.Json
permite até 16 dígitos (para dar suporte à interoperabilidade com outras estruturas e linguagens de programação), mas analisa apenas os sete primeiros. JsonException será gerado se houver mais de 16 segundos dígitos fracionários ao gravar instânciasDateTime
eDateTimeOffset
. - O RFC 3339 permite que os caracteres "T" e "Z" sejam "t" ou "z", respectivamente, mas permite que os aplicativos limitem o suporte apenas às variantes em maiúsculas. A implementação em
System.Text.Json
exige que eles sejam "T" e "Z". JsonException será gerado se as cargas de entrada contiverem "t" ou "z" ao ler as instânciasDateTime
eDateTimeOffset
. - O RFC 3339 especifica que as seções de data e hora são separadas por "T", mas permite que os aplicativos os separem por um espaço (" ").
System.Text.Json
requer que as seções de data e hora sejam separadas por "T". JsonException será gerado se as cargas de entrada contiverem um espaço (" ") ao ler as instânciasDateTime
eDateTimeOffset
.
Se houver frações decimais por segundos, deve haver pelo menos um dígito. 2019-07-26T00:00:00.
não é permitido.
Enquanto até 16 dígitos fracionários são permitidos, apenas os sete primeiros são analisados. Qualquer coisa além disso é considerada zero.
Por exemplo, 2019-07-26T00:00:00.1234567890
será analisado como se fosse 2019-07-26T00:00:00.1234567
.
Essa abordagem se mantém compatível com a implementação de DateTime, que é limitada a essa resolução.
Não há suporte para segundos bissextos.
Suporte para formatação
Os seguintes níveis de granularidade são definidos para formatação:
"'Full date''T''Partial time'"
"yyyy'-'MM'-'dd'T'HH':'mm':'ss" (Especificador de formato ("s") classificável)
Usado para formatar DateTime sem segundos fracionários e sem informações de diferença.
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"
Usado para formatar DateTime com segundos fracionários, mas sem informações de diferença.
'Date time'
"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
Usado para formatar DateTime sem segundos fracionários, mas com uma diferença UTC.
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ"
Usado para formatar DateTime com segundos fracionários e com uma diferença UTC.
"yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm"
Usado para formatar DateTime ou DateTimeOffset sem segundos fracionários, mas com uma diferença local.
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"
Usado para formatar DateTime ou DateTimeOffset com segundos fracionários e com uma diferença local.
Esse nível de granularidade está em conformidade com o RFC 3339.
Se a representação de formato de ida e volta de uma instância DateTime ou DateTimeOffset tiver zeros à direita em seus segundos fracionários, JsonSerializer e Utf8JsonWriter formatará uma representação da instância sem zeros à direita.
Por exemplo, uma instância DateTime cuja representação de formato de ida e volta é 2019-04-24T14:50:17.1010000Z
, será formatada como 2019-04-24T14:50:17.101Z
por JsonSerializer e Utf8JsonWriter.
Se a representação de formato de ida e volta de uma instância DateTime ou DateTimeOffset tiver todos os zeros em seus segundos fracionários, JsonSerializer e Utf8JsonWriter formatará uma representação da instância sem segundos fracionários.
Por exemplo, uma instância DateTime cuja representação de formato de ida e volta é 2019-04-24T14:50:17.0000000+02:00
, será formatada como 2019-04-24T14:50:17+02:00
por JsonSerializer e Utf8JsonWriter.
Truncar zeros em dígitos de segundos fracionários permite que a menor saída necessária para preservar as informações em uma viagem de ida e volta seja gravada.
Um máximo de sete dígitos fracionários são gravados. Esse valor máximo se alinha à implementação DateTime, que é limitada a essa resolução.