Skip to content

KubernetesDateTimeOffsetConverter fails to parse valid RFC3339 timestamps with +00:00 timezone offset #1704

@luiscantero

Description

@luiscantero

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions