From b7cbecc61d80253fe7ff6eeebded07ea89e5bcf8 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Sun, 14 Sep 2025 19:56:40 +0200 Subject: [PATCH] Add DateTime to/from conversion for all known formats to fix parsing and CSV export --- .../AliasClientDb/AliasClientDbContext.cs | 70 ++++++++++++++++--- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/apps/server/Databases/AliasClientDb/AliasClientDbContext.cs b/apps/server/Databases/AliasClientDb/AliasClientDbContext.cs index ab69d1c42..1789f6982 100644 --- a/apps/server/Databases/AliasClientDb/AliasClientDbContext.cs +++ b/apps/server/Databases/AliasClientDb/AliasClientDbContext.cs @@ -111,16 +111,8 @@ public class AliasClientDbContext : DbContext // TODO: when the birthdate field is made optional in data model and all existing values have been converted from "yyyy-MM-dd HH.mm.ss" to "yyyy-MM-dd HH':'mm':'ss", this can probably // be removed. But test the usecase where the birthdate field is empty string (because of browser extension error). var emptyDateTimeConverter = new ValueConverter( - v => v == DateTime.MinValue - ? string.Empty - : v.ToString("yyyy-MM-dd HH':'mm':'ss", CultureInfo.InvariantCulture), - v => string.IsNullOrEmpty(v) - ? DateTime.MinValue - : DateTime.ParseExact( - v, - new[] { "yyyy-MM-dd HH':'mm':'ss", "yyyy-MM-dd HH.mm.ss" }, // Support reading . and : as separators. - CultureInfo.InvariantCulture, - DateTimeStyles.None)); + v => DateTimeToString(v), + v => StringToDateTime(v)); modelBuilder.Entity() .Property(e => e.BirthDate) @@ -187,6 +179,12 @@ public class AliasClientDbContext : DbContext base.OnConfiguring(optionsBuilder); } + /// + /// Gets the options for the AliasClientDbContext. + /// + /// The SQLite connection to use to connect to the SQLite database. + /// The action to perform for logging. + /// The options for the AliasClientDbContext. private static DbContextOptions GetOptions(SqliteConnection connection, Action logAction) { var optionsBuilder = new DbContextOptionsBuilder(); @@ -195,4 +193,56 @@ public class AliasClientDbContext : DbContext return optionsBuilder.Options; } + + /// + /// Converts a DateTime to a string. + /// + /// The DateTime to convert. + /// The string representation of the DateTime. + private static string DateTimeToString(DateTime v) + { + return v == DateTime.MinValue ? string.Empty : v.ToString("yyyy-MM-dd HH':'mm':'ss", CultureInfo.InvariantCulture); + } + + /// + /// Converts a string to a DateTime. + /// + /// The string to convert. + /// The DateTime representation of the string. + private static DateTime StringToDateTime(string v) + { + if (string.IsNullOrEmpty(v)) + { + return DateTime.MinValue; + } + + // Try to parse with all known formats first + string[] formats = new[] + { + "yyyy-MM-dd HH':'mm':'ss", + "yyyy-MM-dd HH.mm.ss", + "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", // ISO 8601 with milliseconds and Zulu + "yyyy-MM-dd'T'HH:mm:ss'Z'", // ISO 8601 with Zulu + "yyyy-MM-dd'T'HH:mm:ss.fff", // ISO 8601 with milliseconds + "yyyy-MM-dd'T'HH:mm:ss", // ISO 8601 basic + "yyyy-MM-dd", // Date only, + }; + + foreach (var format in formats) + { + if (DateTime.TryParseExact(v, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var dt)) + { + return dt; + } + } + + // Fallback: try to parse dynamically (handles most .NET and JS date strings) + if (DateTime.TryParse(v, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var dynamicDt)) + { + return dynamicDt; + } + + // If all parsing fails, return MinValue as a safe fallback + return DateTime.MinValue; + } }