Files
EugeneTes fd6b9027b6 fix(scheduler): address review findings
- JobScheduler: a stored job whose zone lookup throws no longer fails StartAsync; timer re-armed under the lock;
  a delivery cut off by shutdown isn't recorded (one-offs fire again after restart); CancelJobAsync in one step
- Schedules.ParseAt: ISO 8601 with a date only (no machine-clock date for a bare 09:00)
- core: a FollowUp prompt that loses the race to a starting run joins it as a follow-up, not steering
- Telegram: TimeZoneFor tolerates non-numeric targets; RunPromptAsync waits only when queued; one agent dir
- ScriptRunner: bad workspace paths are Failed, not exceptions; zone id helpers share TimeZoneIds.ToIana's rule
- tests: no wall-clock sleeps in scheduler tests; transcript-entry, disabled-scheduler and failing-prompt coverage
- docs: spec notes for the above; README row for the scheduler
2026-09-27 10:56:45 +02:00

23 lines
1.1 KiB
C#

namespace Volcanic.AgenticCore.Extensions.Scheduler;
/// <summary>
/// Zone ids for display and <c>TZ</c>. Mirrors the core's <c>Volcanic.AgenticCore.Hosting.TimeZoneIds</c> (the extension
/// references only the Abstractions); keep the two in step.
/// </summary>
internal static class ZoneNames
{
/// <summary>
/// The IANA id (converting a Windows id), or null when there is none that resolves back to a system zone (an unnamed
/// <c>Local</c> zone: <c>TZ=Local</c> would run the script in UTC). Same rule as <c>TimeZoneIds.ToIana</c>.
/// </summary>
public static string? Iana(TimeZoneInfo zone)
{
if (string.Equals(zone.Id, "Local", StringComparison.OrdinalIgnoreCase)) return null;
var id = zone.HasIanaId ? zone.Id : TimeZoneInfo.TryConvertWindowsIdToIanaId(zone.Id, out var iana) ? iana : null;
return id is not null && TimeZoneInfo.TryFindSystemTimeZoneById(id, out _) ? id : null;
}
/// <summary>The IANA id when there is one, else the zone's own id (<c>TimeZoneIds.Name</c>).</summary>
public static string Name(TimeZoneInfo zone) => Iana(zone) ?? zone.Id;
}