-
Notifications
You must be signed in to change notification settings - Fork 311
Description
Description
The KubernetesDateTimeOffsetConverter in KubernetesJson.cs throws a FormatException when deserializing DateTime fields that use the +00:00 timezone offset notation instead of Z. Both formats are valid RFC3339 and semantically equivalent (both represent UTC), but the converter only accepts the Z suffix.
Version Information
- KubernetesClient NuGet Package Version: 18.0.13
- Target Framework: .NET 10.0
Error Message
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter`5.TryRead[TArg](ReadStack& state, Utf8JsonReader& reader, JsonParameterInfo jsonParameterInfo, TArg& arg)
at System.Text.Json.Serialization.Converters.SmallObjectWithParameterizedConstructorConverter`5.ReadAndCacheConstructorArgument(ReadStack& state, Utf8JsonReader& reader, JsonParameterInfo jsonParameterInfo)
at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.ReadConstructorArgumentsWithContinuation(ReadStack& state, Utf8JsonReader& reader, JsonSerializerOptions options)
at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize[TReadBufferState,TStream](TReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack, T& value)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Stream utf8Json)
at k8s.KubernetesJson.Deserialize[TValue](Stream json, JsonSerializerOptions jsonSerializerOptions)
at k8s.Kubernetes.CreateResultAsync[T](HttpRequestMessage httpRequest, HttpResponseMessage httpResponse, Nullable`1 watch, CancellationToken cancellationToken)
at k8s.AbstractKubernetes.ICustomObjectsOperations_GetNamespacedCustomObjectWithHttpMessagesAsync[T](String group, String version, String namespaceParameter, String plural, String name, IReadOnlyDictionary`2 customHeaders, CancellationToken cancellationToken)
at k8s.AbstractKubernetes.k8s.ICustomObjectsOperations.GetNamespacedCustomObjectWithHttpMessagesAsync[T](String group, String version, String namespaceParameter, String plural, String name, IReadOnlyDictionary`2 customHeaders, CancellationToken cancellationToken)
at k8s.CustomObjectsOperationsExtensions.GetNamespacedCustomObjectAsync[T](ICustomObjectsOperations operations, String group, String version, String namespaceParameter, String plural, String name, CancellationToken cancellationToken)
Root Cause
The KubernetesDateTimeOffsetConverter.Read() method uses DateTimeOffset.TryParseExact with format strings that only accept Z as the UTC designator:
private const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffZ";
private const string RFC3339NanoFormat = "yyyy-MM-dd'T'HH':'mm':'ss.fffffffZ";
private const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ";These formats do not handle +00:00, +05:30, -08:00, or any other valid RFC3339 timezone offset notation.
Workaround
Downgrade to 18.0.5
Minimal Reproduction
Copied from KubernetesJson.cs
using System;
using System.Globalization;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
const string RFC3339MicroFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffZ";
const string RFC3339NanoFormat = "yyyy-MM-dd'T'HH':'mm':'ss.fffffffZ";
const string RFC3339Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ";
// This works - Z suffix
var json1 = "2025-12-12T16:16:55.079293Z";
// This fails - +00:00 offset (semantically identical to Z)
var json2 = "2025-12-12T16:16:55.079293+00:00";
var str = json2;
if (DateTimeOffset.TryParseExact(str, new[] { RFC3339Format, RFC3339MicroFormat }, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
{
Console.WriteLine("It works 1!");
return;
}
// try RFC3339NanoLenient by trimming 1-9 digits to 7 digits
var originalstr = str;
str = Regex.Replace(str, @"\.\d+", m => (m.Value + "000000000").Substring(0, 7 + 1)); // 7 digits + 1 for the dot
if (DateTimeOffset.TryParseExact(str, new[] { RFC3339NanoFormat }, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
{
Console.WriteLine("It works 2!");
return;
}
throw new FormatException($"Unable to parse {originalstr} as RFC3339 RFC3339Micro or RFC3339Nano");
}
}Expected Behavior
The converter should successfully parse valid RFC3339 timestamps regardless of whether they use:
Z (Zulu/UTC designator)
+00:00 (explicit UTC offset)
+05:30, -08:00, etc. (other timezone offsets)
Per RFC3339 Section 4.3, +00:00 is a valid representation of UTC.
Suggested Fix
Update the format strings to use K or zzz format specifier which handles timezone offsets, or normalize +00:00 to Z before parsing:
Let .NET do the final check
... DateTimeOffset.TryParse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out result) ...
// Or:
try
{
var value = System.Text.Json.JsonSerializer.Deserialize<DateTimeOffset>($@"""{str}""");
Console.WriteLine("It works 3!");
return;
}
catch { }Context
This issue occurs when working with Kubernetes Custom Resources (CRDs) where some controllers/operators serialize DateTime fields using +00:00 instead of Z. The standard .NET DateTime.ToString("o") format produces +00:00 for UTC times, making this a common scenario.