namespace Aeshnidae.Trials;
///
/// The trophy week. An ISO week key ("2026-W38") whose boundary is the award moment
/// (Settings.AwardDay / AwardHour, server-local) rather than Monday midnight: every
/// server-local time is shifted forward by the distance from the award moment to the
/// following Monday 00:00 before the ISO week is read, so a clear at Sunday 18:00
/// belongs to the week that has just begun and the award at Sunday 17:00 closes a
/// week that ends exactly then. With Sunday 17:00 the shift is seven hours.
///
public static class Weeks
{
/// From the award moment to the next Monday 00:00.
public static TimeSpan Shift(Settings s)
{
var day = (int)s.AwardDayOfWeek; // Sunday 0 .. Saturday 6
var hour = Math.Clamp(s.AwardHour, 0, 23);
// Days from the award day to Monday, going forward: Sunday -> 1, Monday -> 7, Tuesday -> 6 ...
var daysToMonday = ((int)DayOfWeek.Monday - day + 7) % 7;
if (daysToMonday == 0)
daysToMonday = 7;
return TimeSpan.FromDays(daysToMonday) - TimeSpan.FromHours(hour);
}
/// The week a server-local time falls in.
public static string KeyOf(DateTime localTime, Settings s)
{
var shifted = localTime + Shift(s);
return $"{ISOWeek.GetYear(shifted)}-W{ISOWeek.GetWeekOfYear(shifted):00}";
}
/// The week that ended at the most recent award moment, from a server-local time.
public static string PreviousKey(DateTime localTime, Settings s) => KeyOf(localTime.AddDays(-7), s);
/// The next award moment after a server-local time.
public static DateTime NextAward(DateTime localTime, Settings s)
{
var hour = Math.Clamp(s.AwardHour, 0, 23);
var candidate = localTime.Date.AddHours(hour);
while (candidate.DayOfWeek != s.AwardDayOfWeek || candidate <= localTime)
candidate = candidate.AddDays(1);
return candidate;
}
/// The award moment that began the current week (the last one that passed).
public static DateTime CurrentWeekStart(DateTime localTime, Settings s) => NextAward(localTime, s).AddDays(-7);
/// The Monday the shifted ISO week of a key starts on, for the human "week of" line.
public static string Describe(string key, Settings s)
{
try
{
var parts = key.Split("-W");
var monday = ISOWeek.ToDateTime(int.Parse(parts[0]), int.Parse(parts[1]), DayOfWeek.Monday);
var start = monday - Shift(s);
return start.ToString("d MMM yyyy", CultureInfo.InvariantCulture);
}
catch
{
return key;
}
}
}