using System.Globalization; using System.Text; namespace Volcanic.AgenticCore.Extensions.Scheduler; /// How jobs and times are shown (the schedule tool, /jobs, zone-change replies). Invariant culture, 24 h. public static class JobFormat { public static string Icon(JobKind kind) => kind switch { JobKind.Reminder => "⏰", JobKind.Prompt => "💬", _ => "🔍" }; /// 27 Sep 09:00 in . public static string DayTime(DateTimeOffset at, TimeZoneInfo zone) => TimeZoneInfo.ConvertTime(at, zone).ToString("d MMM HH:mm", CultureInfo.InvariantCulture); /// 14:10 when on the same local day as , else . public static string When(DateTimeOffset at, TimeZoneInfo zone, DateTimeOffset now) { var local = TimeZoneInfo.ConvertTime(at, zone); return local.Date == TimeZoneInfo.ConvertTime(now, zone).Date ? local.ToString("HH:mm", CultureInfo.InvariantCulture) : DayTime(at, zone); } /// Sat 2026-09-26 14:03 (the model-facing form: weekday and full date). public static string Stamp(DateTimeOffset at, TimeZoneInfo zone) => TimeZoneInfo.ConvertTime(at, zone).ToString("ddd yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture); public static string ZoneName(TimeZoneInfo zone) => ZoneNames.Name(zone); /// exit 1, exit 1, timed out, timed out, or did not start. public static string FailureReason(JobResult result) => result switch { { ExitCode: { } code, TimedOut: true } => $"exit {code.ToString(CultureInfo.InvariantCulture)}, timed out", { ExitCode: { } code } => $"exit {code.ToString(CultureInfo.InvariantCulture)}", { TimedOut: true } => "timed out", _ => "did not start", }; /// `k7m2q` ⏰ Call the dentist · once 27 Sep 09:00; cron jobs show the expression and the next run; a failed last run is appended. public static string Line(JobView view, TimeZoneInfo zone, DateTimeOffset now) { var job = view.Job; var sb = new StringBuilder().Append('`').Append(job.Id).Append("` ").Append(Icon(job.Kind)).Append(' ').Append(job.Name); if (job.Schedule.At is { } at) sb.Append(" · once ").Append(DayTime(at, zone)); else { sb.Append(" · `").Append(job.Schedule.Cron).Append('`'); if (view.NextRunAt is { } next) sb.Append(" · next ").Append(When(next, zone, now)); } if (job.LastResult is { Ok: false } failed) sb.Append(" · last: failed (").Append(FailureReason(failed)).Append(')'); return sb.ToString(); } }