diff --git a/src/GitLabApiClient/GitLabApiClient.csproj b/src/GitLabApiClient/GitLabApiClient.csproj
index 404739c4..9e8a2335 100644
--- a/src/GitLabApiClient/GitLabApiClient.csproj
+++ b/src/GitLabApiClient/GitLabApiClient.csproj
@@ -4,7 +4,7 @@
net8.0
10
Apiiro.GitLabApiClient
- 0.1.44
+ 0.1.46
Apiiro
Apiiro
GitLabApiClient
diff --git a/src/GitLabApiClient/Internal/Http/Serialization/TolerantNullableDateTimeConverter.cs b/src/GitLabApiClient/Internal/Http/Serialization/TolerantNullableDateTimeConverter.cs
new file mode 100644
index 00000000..a9ab827c
--- /dev/null
+++ b/src/GitLabApiClient/Internal/Http/Serialization/TolerantNullableDateTimeConverter.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Globalization;
+using Newtonsoft.Json;
+
+namespace GitLabApiClient.Internal.Http.Serialization
+{
+ internal sealed class TolerantNullableDateTimeConverter : JsonConverter
+ {
+ public override bool CanConvert(Type objectType) =>
+ objectType == typeof(DateTime?) || objectType == typeof(DateTime);
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ switch (reader.TokenType)
+ {
+ case JsonToken.Null:
+ return null;
+ case JsonToken.Date:
+ return reader.Value is DateTime dateTime ? dateTime : (DateTime?)null;
+ case JsonToken.String:
+ return DateTime.TryParse((string)reader.Value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsed)
+ ? parsed
+ : (DateTime?)null;
+ default:
+ return null;
+ }
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ if (value is DateTime dateTime)
+ {
+ writer.WriteValue(dateTime);
+ }
+ else
+ {
+ writer.WriteNull();
+ }
+ }
+ }
+}
diff --git a/src/GitLabApiClient/Models/Member.cs b/src/GitLabApiClient/Models/Member.cs
index dd63ca43..fbbd02b2 100644
--- a/src/GitLabApiClient/Models/Member.cs
+++ b/src/GitLabApiClient/Models/Member.cs
@@ -1,4 +1,5 @@
using System;
+using GitLabApiClient.Internal.Http.Serialization;
using Newtonsoft.Json;
namespace GitLabApiClient.Models
@@ -9,6 +10,7 @@ public sealed class Member : Account
public int AccessLevel { get; set; }
[JsonProperty("expires_at")]
- public DateTime ExpiresAt { get; set; }
+ [JsonConverter(typeof(TolerantNullableDateTimeConverter))]
+ public DateTime? ExpiresAt { get; set; }
}
}
diff --git a/test/GitLabApiClient.Test/Internal/MemberExpiresAtTest.cs b/test/GitLabApiClient.Test/Internal/MemberExpiresAtTest.cs
new file mode 100644
index 00000000..189a1de1
--- /dev/null
+++ b/test/GitLabApiClient.Test/Internal/MemberExpiresAtTest.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using FluentAssertions;
+using GitLabApiClient.Models;
+using Newtonsoft.Json;
+using Xunit;
+
+namespace GitLabApiClient.Test.Internal
+{
+ public class MemberExpiresAtTest
+ {
+ [Fact]
+ public void UnparseableExpiresAt_YieldsNull_AndRestOfListStillDeserializes()
+ {
+ const string json = @"[
+ { ""id"": 1, ""username"": ""valid"", ""access_level"": 30, ""expires_at"": ""2025-12-04"" },
+ { ""id"": 2, ""username"": ""broken"", ""access_level"": 40, ""expires_at"": ""20235-12-04"" }
+ ]";
+
+ var members = JsonConvert.DeserializeObject>(json);
+
+ members.Should().HaveCount(2);
+ members[0].ExpiresAt.Should().Be(new DateTime(2025, 12, 4));
+ members[1].Username.Should().Be("broken");
+ members[1].ExpiresAt.Should().BeNull();
+ }
+
+ [Fact]
+ public void NullExpiresAt_YieldsNull()
+ {
+ const string json = @"{ ""id"": 1, ""access_level"": 30, ""expires_at"": null }";
+
+ var member = JsonConvert.DeserializeObject(json);
+
+ member.ExpiresAt.Should().BeNull();
+ }
+
+ [Fact]
+ public void MissingExpiresAt_YieldsNull()
+ {
+ const string json = @"{ ""id"": 1, ""access_level"": 30 }";
+
+ var member = JsonConvert.DeserializeObject(json);
+
+ member.ExpiresAt.Should().BeNull();
+ }
+
+ [Fact]
+ public void ValidExpiresAt_IsParsed()
+ {
+ const string json = @"{ ""id"": 1, ""access_level"": 30, ""expires_at"": ""2025-12-04"" }";
+
+ var member = JsonConvert.DeserializeObject(json);
+
+ member.ExpiresAt.Should().Be(new DateTime(2025, 12, 4));
+ }
+ }
+}