From 6985678d2fcab3a0bb29a70131f58f91225fde1d Mon Sep 17 00:00:00 2001 From: Paul Koudelka Date: Mon, 10 Aug 2026 20:06:51 +0200 Subject: [PATCH] changed DateTime to DateTimeOffset --- .../EmbeddingState/EmbeddingStateClient.cs | 14 ++--- .../EmbeddingState/EmbeddingStateDbContext.cs | 53 +++++++++---------- .../EmbeddingStateDbContextModelSnapshot.cs | 34 ++++++------ ...qliteEmbeddingStateClientImplementation.cs | 6 +-- .../VectorStore/VectorStoragePoint.cs | 6 +-- .../Services/DataSourceEmbeddingModels.cs | 4 +- .../DataSourceEmbeddingService.Files.cs | 8 +-- .../Services/DataSourceEmbeddingService.cs | 16 +++--- 8 files changed, 70 insertions(+), 71 deletions(-) diff --git a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateClient.cs b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateClient.cs index 4cf8e513..775b05c5 100644 --- a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateClient.cs +++ b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateClient.cs @@ -39,9 +39,9 @@ public sealed record EmbeddingStateFile( string FileType, string Fingerprint, long FileSize, - DateTime CreationUtc, - DateTime LastWriteUtc, - DateTime EmbeddedAtUtc, + DateTimeOffset CreationUtc, + DateTimeOffset LastWriteUtc, + DateTimeOffset EmbeddedAtUtc, int ChunkCount, string ComplianceLevel, int ComplianceLevelRank); @@ -52,7 +52,7 @@ public sealed record EmbeddingStateChunk( int? PageNumber, int ChunkIndex, string ChunkText, - DateTime EmbeddedAtUtc); + DateTimeOffset EmbeddedAtUtc); public sealed record EmbeddingStateSearchResult( string ChunkId, @@ -70,9 +70,9 @@ public sealed record EmbeddingStateSearchResult( double Score, string Fingerprint, long FileSize, - DateTime CreationUtc, - DateTime LastWriteUtc, - DateTime EmbeddedAtUtc, + DateTimeOffset CreationUtc, + DateTimeOffset LastWriteUtc, + DateTimeOffset EmbeddedAtUtc, int ChunkCount, string ComplianceLevel, int ComplianceLevelRank); diff --git a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateDbContext.cs b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateDbContext.cs index 9088f9cf..4c4a353e 100644 --- a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateDbContext.cs +++ b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/EmbeddingStateDbContext.cs @@ -22,7 +22,7 @@ internal sealed class EmbeddingStateDbContext(DbContextOptions(entity => { @@ -36,7 +36,7 @@ internal sealed class EmbeddingStateDbContext(DbContextOptions dataSource.EmbeddingSignature).HasColumnName("embedding_signature").IsRequired(); entity.Property(dataSource => dataSource.SourceHash).HasColumnName("source_hash").IsRequired().HasDefaultValue(string.Empty); entity.Property(dataSource => dataSource.VectorSize).HasColumnName("vector_size").HasDefaultValue(0); - entity.Property(dataSource => dataSource.UpdatedAtUtc).HasColumnName("updated_at_utc").HasConversion(utcDateTimeConverter).IsRequired(); + entity.Property(dataSource => dataSource.UpdatedAtUtc).HasColumnName("updated_at_utc").HasConversion(utcDateTimeOffsetConverter).IsRequired(); entity .HasMany(dataSource => dataSource.Files) @@ -58,9 +58,9 @@ internal sealed class EmbeddingStateDbContext(DbContextOptions file.FileType).HasColumnName("file_type").IsRequired(); entity.Property(file => file.Fingerprint).HasColumnName("fingerprint").IsRequired(); entity.Property(file => file.FileSize).HasColumnName("file_size"); - entity.Property(file => file.CreationUtc).HasColumnName("creation_utc").HasConversion(utcDateTimeConverter).IsRequired(); - entity.Property(file => file.LastWriteUtc).HasColumnName("last_write_utc").HasConversion(utcDateTimeConverter).IsRequired(); - entity.Property(file => file.EmbeddedAtUtc).HasColumnName("embedded_at_utc").HasConversion(utcDateTimeConverter).IsRequired(); + entity.Property(file => file.CreationUtc).HasColumnName("creation_utc").HasConversion(utcDateTimeOffsetConverter).IsRequired(); + entity.Property(file => file.LastWriteUtc).HasColumnName("last_write_utc").HasConversion(utcDateTimeOffsetConverter).IsRequired(); + entity.Property(file => file.EmbeddedAtUtc).HasColumnName("embedded_at_utc").HasConversion(utcDateTimeOffsetConverter).IsRequired(); entity.Property(file => file.ChunkCount).HasColumnName("chunk_count"); entity.Property(file => file.ComplianceLevel).HasColumnName("compliance_level").IsRequired(); entity.Property(file => file.ComplianceLevelRank).HasColumnName("compliance_level_rank"); @@ -89,7 +89,7 @@ internal sealed class EmbeddingStateDbContext(DbContextOptions chunk.PageNumber).HasColumnName("page_number"); entity.Property(chunk => chunk.ChunkIndex).HasColumnName("chunk_index"); entity.Property(chunk => chunk.ChunkText).HasColumnName("chunk_text").IsRequired(); - entity.Property(chunk => chunk.EmbeddedAtUtc).HasColumnName("embedded_at_utc").HasConversion(utcDateTimeConverter).IsRequired(); + entity.Property(chunk => chunk.EmbeddedAtUtc).HasColumnName("embedded_at_utc").HasConversion(utcDateTimeOffsetConverter).IsRequired(); entity.HasIndex(chunk => chunk.ChunkId).HasDatabaseName("idx_embedding_chunks_chunk_id").IsUnique(); entity.HasIndex(chunk => chunk.ParentFileId).HasDatabaseName("idx_embedding_chunks_parent_file"); @@ -102,9 +102,9 @@ internal sealed class EmbeddingStateDbContext(DbContextOptions result.CreationUtc).HasConversion(utcDateTimeConverter); - entity.Property(result => result.LastWriteUtc).HasConversion(utcDateTimeConverter); - entity.Property(result => result.EmbeddedAtUtc).HasConversion(utcDateTimeConverter); + entity.Property(result => result.CreationUtc).HasConversion(utcDateTimeOffsetConverter); + entity.Property(result => result.LastWriteUtc).HasConversion(utcDateTimeOffsetConverter); + entity.Property(result => result.EmbeddedAtUtc).HasConversion(utcDateTimeOffsetConverter); }); } @@ -134,7 +134,7 @@ internal sealed class EmbeddingStateDataSourceEntity public int VectorSize { get; set; } - public DateTime UpdatedAtUtc { get; set; } + public DateTimeOffset UpdatedAtUtc { get; set; } public List Files { get; set; } = []; } @@ -157,11 +157,11 @@ internal sealed class EmbeddingStateFileEntity public long FileSize { get; set; } - public DateTime CreationUtc { get; set; } + public DateTimeOffset CreationUtc { get; set; } - public DateTime LastWriteUtc { get; set; } + public DateTimeOffset LastWriteUtc { get; set; } - public DateTime EmbeddedAtUtc { get; set; } + public DateTimeOffset EmbeddedAtUtc { get; set; } public int ChunkCount { get; set; } @@ -188,7 +188,7 @@ internal sealed class EmbeddingStateChunkEntity public string ChunkText { get; set; } = string.Empty; - public DateTime EmbeddedAtUtc { get; set; } + public DateTimeOffset EmbeddedAtUtc { get; set; } public EmbeddingStateFileEntity? File { get; set; } } @@ -225,11 +225,11 @@ internal sealed class EmbeddingStateSearchResultEntity public long FileSize { get; set; } - public DateTime CreationUtc { get; set; } + public DateTimeOffset CreationUtc { get; set; } - public DateTime LastWriteUtc { get; set; } + public DateTimeOffset LastWriteUtc { get; set; } - public DateTime EmbeddedAtUtc { get; set; } + public DateTimeOffset EmbeddedAtUtc { get; set; } public int ChunkCount { get; set; } @@ -238,22 +238,21 @@ internal sealed class EmbeddingStateSearchResultEntity public int ComplianceLevelRank { get; set; } } -internal sealed class EmbeddingStateDateTimeConverter() : ValueConverter( - value => EmbeddingStateDateTime.ToUtcText(value), - value => EmbeddingStateDateTime.ParseUtc(value)); +internal sealed class EmbeddingStateDateTimeOffsetConverter() : ValueConverter( + value => EmbeddingStateDateTimeOffset.ToUtcText(value), + value => EmbeddingStateDateTimeOffset.ParseUtc(value)); -internal static class EmbeddingStateDateTime +internal static class EmbeddingStateDateTimeOffset { - public static string ToUtcText(DateTime dateTime) + public static string ToUtcText(DateTimeOffset dateTime) { - var utc = dateTime.Kind is DateTimeKind.Utc ? dateTime : dateTime.ToUniversalTime(); - return utc.ToString("O", CultureInfo.InvariantCulture); + return dateTime.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture); } - public static DateTime ParseUtc(string value) + public static DateTimeOffset ParseUtc(string value) { - return DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime) + return DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTime) ? dateTime.ToUniversalTime() - : DateTime.UnixEpoch; + : DateTimeOffset.UnixEpoch; } } diff --git a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/Migrations/EmbeddingStateDbContextModelSnapshot.cs b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/Migrations/EmbeddingStateDbContextModelSnapshot.cs index 98910680..c1209ec8 100644 --- a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/Migrations/EmbeddingStateDbContextModelSnapshot.cs +++ b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/Migrations/EmbeddingStateDbContextModelSnapshot.cs @@ -12,7 +12,7 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot { #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "9.0.18"); - var utcDateTimeConverter = new EmbeddingStateDateTimeConverter(); + var utcDateTimeOffsetConverter = new EmbeddingStateDateTimeOffsetConverter(); modelBuilder.Entity("AIStudio.Tools.Databases.EmbeddingState.EmbeddingStateDataSourceEntity", entity => { @@ -46,8 +46,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot .HasColumnName("source_hash") .HasDefaultValue(string.Empty); - entity.Property("UpdatedAtUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("UpdatedAtUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT") .HasColumnName("updated_at_utc"); @@ -86,8 +86,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot .HasColumnType("INTEGER") .HasColumnName("compliance_level_rank"); - entity.Property("CreationUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("CreationUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT") .HasColumnName("creation_utc"); @@ -96,8 +96,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot .HasColumnType("TEXT") .HasColumnName("data_source_id"); - entity.Property("EmbeddedAtUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("EmbeddedAtUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT") .HasColumnName("embedded_at_utc"); @@ -120,8 +120,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot .HasColumnType("TEXT") .HasColumnName("fingerprint"); - entity.Property("LastWriteUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("LastWriteUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT") .HasColumnName("last_write_utc"); @@ -173,8 +173,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot .HasColumnType("TEXT") .HasColumnName("chunk_text"); - entity.Property("EmbeddedAtUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("EmbeddedAtUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT") .HasColumnName("embedded_at_utc"); @@ -233,8 +233,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot entity.Property("ComplianceLevelRank") .HasColumnType("INTEGER"); - entity.Property("CreationUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("CreationUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT"); entity.Property("DataSourceId") @@ -249,8 +249,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot .IsRequired() .HasColumnType("TEXT"); - entity.Property("EmbeddedAtUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("EmbeddedAtUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT"); entity.Property("FileName") @@ -268,8 +268,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot .IsRequired() .HasColumnType("TEXT"); - entity.Property("LastWriteUtc") - .HasConversion(utcDateTimeConverter) + entity.Property("LastWriteUtc") + .HasConversion(utcDateTimeOffsetConverter) .HasColumnType("TEXT"); entity.Property("PageNumber") diff --git a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/SqliteEmbeddingStateClientImplementation.cs b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/SqliteEmbeddingStateClientImplementation.cs index 93657a1c..ce187c3b 100644 --- a/app/MindWork AI Studio/Tools/Databases/EmbeddingState/SqliteEmbeddingStateClientImplementation.cs +++ b/app/MindWork AI Studio/Tools/Databases/EmbeddingState/SqliteEmbeddingStateClientImplementation.cs @@ -139,7 +139,7 @@ public sealed class SqliteEmbeddingStateClientImplementation( return; dataSource.VectorSize = vectorSize; - dataSource.UpdatedAtUtc = DateTime.UtcNow; + dataSource.UpdatedAtUtc = DateTimeOffset.UtcNow; await context.SaveChangesAsync(token); } @@ -151,7 +151,7 @@ public sealed class SqliteEmbeddingStateClientImplementation( return; dataSource.SourceHash = sourceHash; - dataSource.UpdatedAtUtc = DateTime.UtcNow; + dataSource.UpdatedAtUtc = DateTimeOffset.UtcNow; await context.SaveChangesAsync(token); } @@ -343,7 +343,7 @@ public sealed class SqliteEmbeddingStateClientImplementation( dataSource.EmbeddingSignature = embeddingSignature; dataSource.SourceHash = sourceHash; dataSource.VectorSize = vectorSize; - dataSource.UpdatedAtUtc = DateTime.UtcNow; + dataSource.UpdatedAtUtc = DateTimeOffset.UtcNow; } private static void ApplyFile(EmbeddingStateFileEntity fileEntity, string dataSourceId, EmbeddingStateFile file) diff --git a/app/MindWork AI Studio/Tools/Databases/VectorStore/VectorStoragePoint.cs b/app/MindWork AI Studio/Tools/Databases/VectorStore/VectorStoragePoint.cs index d6695c18..f224dc0e 100644 --- a/app/MindWork AI Studio/Tools/Databases/VectorStore/VectorStoragePoint.cs +++ b/app/MindWork AI Studio/Tools/Databases/VectorStore/VectorStoragePoint.cs @@ -17,8 +17,8 @@ public sealed record VectorStoragePoint( int ChunkIndex, string Text, string Fingerprint, - DateTime CreationUtc, - DateTime LastWriteUtc, - DateTime EmbeddedAtUtc, + DateTimeOffset CreationUtc, + DateTimeOffset LastWriteUtc, + DateTimeOffset EmbeddedAtUtc, string ComplianceLevel, int ComplianceLevelRank); diff --git a/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingModels.cs b/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingModels.cs index b300115e..917af1aa 100644 --- a/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingModels.cs +++ b/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingModels.cs @@ -92,6 +92,6 @@ public sealed class DataSourceEmbeddingManifest public sealed record EmbeddedFileRecord( string Fingerprint, long FileSize, - DateTime LastWriteUtc, - DateTime EmbeddedAtUtc, + DateTimeOffset LastWriteUtc, + DateTimeOffset EmbeddedAtUtc, int ChunkCount); diff --git a/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.Files.cs b/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.Files.cs index ee4a2b07..dff7f369 100644 --- a/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.Files.cs +++ b/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.Files.cs @@ -1105,7 +1105,7 @@ public sealed partial class DataSourceEmbeddingService return Convert.ToHexString(bytes); } - private EmbeddingStateFile CreateEmbeddingStateFile(IDataSource dataSource, FileInfo file, string fingerprint, int chunkCount, DateTime embeddedAtUtc) + private EmbeddingStateFile CreateEmbeddingStateFile(IDataSource dataSource, FileInfo file, string fingerprint, int chunkCount, DateTimeOffset embeddedAtUtc) { file.Refresh(); var absolutePath = Path.GetFullPath(file.FullName); @@ -1118,15 +1118,15 @@ public sealed partial class DataSourceEmbeddingService GetFileType(file), fingerprint, file.Exists ? file.Length : 0, - file.Exists ? file.CreationTimeUtc : DateTime.UnixEpoch, - file.Exists ? file.LastWriteTimeUtc : DateTime.UnixEpoch, + file.Exists ? new DateTimeOffset(file.CreationTimeUtc) : DateTimeOffset.UnixEpoch, + file.Exists ? new DateTimeOffset(file.LastWriteTimeUtc) : DateTimeOffset.UnixEpoch, embeddedAtUtc, chunkCount, complianceLevel.ToString(), (int)complianceLevel); } - private IReadOnlyList CreateEmbeddingStateChunks(EmbeddingStateFile parentFile, IReadOnlyList batch, DateTime embeddedAtUtc) + private IReadOnlyList CreateEmbeddingStateChunks(EmbeddingStateFile parentFile, IReadOnlyList batch, DateTimeOffset embeddedAtUtc) { return batch .Select(chunk => new EmbeddingStateChunk( diff --git a/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.cs b/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.cs index 42ccb0bb..7acadb15 100644 --- a/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.cs +++ b/app/MindWork AI Studio/Tools/Services/DataSourceEmbeddingService.cs @@ -567,18 +567,18 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM ShortHash(fingerprint), skippedFiles + completedFiles + 1, totalFiles); - var startedAtUtc = DateTime.UtcNow; + var startedAtUtc = DateTimeOffset.UtcNow; var chunkCount = await this.IndexOneFileAsync(embeddingState, vectorStore, dataSource, file, fingerprint, embeddingProvider, provider, manifest, optimizationTracker, token); token.ThrowIfCancellationRequested(); var fingerprintAfterEmbedding = BuildFileMetadataHash(file); if (!string.Equals(fingerprint, fingerprintAfterEmbedding, StringComparison.Ordinal)) throw new IOException($"The file '{file.FullName}' changed while it was being embedded. Its partial embeddings will be discarded and the file will be retried on the next refresh."); - var embeddedAtUtc = DateTime.UtcNow; + var embeddedAtUtc = DateTimeOffset.UtcNow; var record = new EmbeddedFileRecord( fingerprint, file.Length, - file.LastWriteTimeUtc, + new DateTimeOffset(file.LastWriteTimeUtc), embeddedAtUtc, chunkCount); await embeddingState.UpsertFileAsync( @@ -598,7 +598,7 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM dataSource.Name, dataSource.Id, chunkCount, - (DateTime.UtcNow - startedAtUtc).TotalMilliseconds); + (DateTimeOffset.UtcNow - startedAtUtc).TotalMilliseconds); } catch (OperationCanceledException) when (token.IsCancellationRequested) { @@ -668,7 +668,7 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM optimizationTracker.MarkChanged(); await embeddingState.DeleteFileAsync(dataSource.Id, file.FullName, token); - var parentFile = this.CreateEmbeddingStateFile(dataSource, file, fingerprint, 0, DateTime.UtcNow); + var parentFile = this.CreateEmbeddingStateFile(dataSource, file, fingerprint, 0, DateTimeOffset.UtcNow); await embeddingState.UpsertFileAsync(dataSource.Id, parentFile, token); var embeddingBatchSize = Math.Max(1, embeddingProvider.EffectiveEmbeddingBatchSize); @@ -769,7 +769,7 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM } token.ThrowIfCancellationRequested(); - var embeddedAtUtc = DateTime.UtcNow; + var embeddedAtUtc = DateTimeOffset.UtcNow; await this.UpsertPointsAsync( vectorStore, collectionName, @@ -820,7 +820,7 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM EmbeddingStateFile parentFile, IReadOnlyList batch, IReadOnlyList> vectors, - DateTime embeddedAtUtc, + DateTimeOffset embeddedAtUtc, CancellationToken token) { var points = batch.Select((item, index) => new VectorStoragePoint( @@ -1150,7 +1150,7 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM if (existingRecord.FileSize != file.Length) reasons.Add($"file size changed from {existingRecord.FileSize} to {file.Length} bytes"); - if (existingRecord.LastWriteUtc != file.LastWriteTimeUtc) + if (existingRecord.LastWriteUtc != new DateTimeOffset(file.LastWriteTimeUtc)) reasons.Add($"last modified time changed from {existingRecord.LastWriteUtc:O} to {file.LastWriteTimeUtc:O}"); return reasons.Count == 0