fix(claude-do): merge ScheduledFor als UTC speichern — geplante Tasks starten aktu
ClaudeDo-Task: ea2b5950-baff-4631-ad0d-6c156a01267e
This commit is contained in:
@@ -121,7 +121,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
|
||||
public bool HasBranch => !string.IsNullOrWhiteSpace(Branch);
|
||||
public bool HasDiff => DiffAdditions > 0 || DiffDeletions > 0;
|
||||
public bool HasSteps => StepsCount > 0;
|
||||
public bool IsOverdue => ScheduledFor is { } d && d.Date < DateTime.Today && !Done;
|
||||
public bool IsOverdue => ScheduledFor is { } d && d.ToLocalTime().Date < DateTime.Today && !Done;
|
||||
public bool IsRunning => Status == TaskStatus.Running;
|
||||
public bool IsWaitingForReview => Status == TaskStatus.WaitingForReview;
|
||||
// Parked = set aside from review: Idle but still holding its Active worktree (vs a plain Idle
|
||||
|
||||
@@ -637,7 +637,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
|
||||
if (r.Done && !underOpenPlanningParent)
|
||||
completed.Add(r);
|
||||
else if (r.ScheduledFor is { } d && d.Date < today)
|
||||
else if (r.ScheduledFor is { } d && d.ToLocalTime().Date < today)
|
||||
overdue.Add(r);
|
||||
else
|
||||
open.Add(r);
|
||||
@@ -1361,12 +1361,18 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
public async Task SetScheduledForAsync(TaskRowViewModel row, DateTime? when)
|
||||
{
|
||||
if (row is null) return;
|
||||
// ThemedDatePicker builds Unspecified/Local wall-clock values; the DB's UtcConverter only
|
||||
// tags DateTimes as Utc on read, it never converts on write, so this is the one place that
|
||||
// must turn "local wall clock" into a real UTC instant before it's persisted.
|
||||
var whenUtc = when is { } w
|
||||
? (w.Kind == DateTimeKind.Utc ? w : DateTime.SpecifyKind(w, DateTimeKind.Local).ToUniversalTime())
|
||||
: (DateTime?)null;
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity is null) return;
|
||||
entity.ScheduledFor = when;
|
||||
entity.ScheduledFor = whenUtc;
|
||||
await db.SaveChangesAsync();
|
||||
row.ScheduledFor = when;
|
||||
row.ScheduledFor = whenUtc;
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
TasksChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
@@ -217,7 +217,7 @@ public partial class TaskRowView : UserControl
|
||||
{
|
||||
if (DataContext is not TaskRowViewModel row) return;
|
||||
_pendingScheduleRow = row;
|
||||
ScheduleDate.SelectedDate = row.ScheduledFor ?? DateTime.Now.AddHours(1);
|
||||
ScheduleDate.SelectedDate = row.ScheduledFor?.ToLocalTime() ?? DateTime.Now.AddHours(1);
|
||||
ScheduleAnchor.Flyout?.ShowAt(ScheduleAnchor);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user