fix(ui): store ScheduledFor as real UTC instead of mislabeled local time

The DB's global UtcConverter only tags DateTimes as Utc on read
(SpecifyKind), it never converts on write. SetScheduledForAsync persisted
the ThemedDatePicker's Local/Unspecified wall-clock value verbatim, so
QueuePicker's comparison against DateTime.UtcNow fired scheduled tasks late
by the local UTC offset (e.g. 2h in CEST). Convert to UTC at the write
boundary, and ToLocalTime() at the read/compare sites (overdue checks in
TaskRowViewModel/TasksIslandViewModel, the date-picker's edit seed) so
existing scheduled/overdue display doesn't shift.

Existing DB rows hold local wall-clock values mistagged as Utc; no
migration added (few rows, one-time 2h-class shift accepted per the
originating audit finding).
This commit is contained in:
Mika Kuns
2026-08-26 15:46:10 +02:00
parent 421f06866e
commit f70aec495b
4 changed files with 31 additions and 5 deletions
@@ -346,4 +346,24 @@ public class TasksIslandRegroupTests : IDisposable
Assert.Equal(new[] { "t2", "t3", "t1" }, vm.Items.Select(r => r.Id));
Assert.Equal(new[] { "t2", "t3", "t1" }, vm.Rows.OfType<TaskRowViewModel>().Select(r => r.Id));
}
[Fact]
public async Task SetScheduledForAsync_LocalDateTime_PersistsAsUtc()
{
await SeedTasksAsync(("t1", TaskStatus.Idle, null, 0));
var vm = BuildViewModel();
await LoadAndWaitAsync(vm, UserList("list1", "Default"));
var row = vm.Items.First(r => r.Id == "t1");
// ThemedDatePicker hands SetScheduledForAsync Local/Unspecified wall-clock values.
var localWhen = DateTime.SpecifyKind(new DateTime(2026, 8, 26, 15, 0, 0), DateTimeKind.Local);
await vm.SetScheduledForAsync(row, localWhen);
await using var db = NewContext();
var entity = await db.Tasks.FirstAsync(t => t.Id == "t1");
Assert.Equal(DateTimeKind.Utc, entity.ScheduledFor!.Value.Kind);
Assert.Equal(localWhen.ToUniversalTime(), entity.ScheduledFor!.Value);
}
}