feat(missions): JstPeriod helper — 02:00 JST anchored day/week/month keys

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-05-27 10:21:51 -04:00
parent 6fbf7cbc94
commit b38be1d953
2 changed files with 105 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
using System.Globalization;
namespace SVSim.EmulatedEntrypoint.Services;
/// <summary>
/// JST-anchored period bucketing for ViewerEventCounters. Day/week/month boundaries are
/// 02:00 JST (matching the real-game daily reset). Pure functions, no dependencies.
/// </summary>
public static class JstPeriod
{
private static readonly TimeSpan Jst = TimeSpan.FromHours(9);
private const string DayPrefix = "day:";
private const string WeekPrefix = "week:";
private const string MonthPrefix = "month:";
public const string AllTime = "all-time";
/// <summary>
/// Converts the given instant to a JST-anchored "effective date" by:
/// 1. Shifting to JST (+09:00)
/// 2. Subtracting 2 hours so anything before 02:00 JST belongs to the previous day
/// </summary>
private static DateTime EffectiveJstDate(DateTimeOffset utcOrAny)
{
var jst = utcOrAny.ToOffset(Jst);
return jst.AddHours(-2).Date;
}
public static string DayKey(DateTimeOffset when)
{
var d = EffectiveJstDate(when);
return $"{DayPrefix}{d:yyyy-MM-dd}";
}
public static string WeekKey(DateTimeOffset when)
{
var d = EffectiveJstDate(when);
var iso = ISOWeek.GetWeekOfYear(d);
var year = ISOWeek.GetYear(d);
return $"{WeekPrefix}{year:D4}-W{iso:D2}";
}
public static string MonthKey(DateTimeOffset when)
{
var d = EffectiveJstDate(when);
return $"{MonthPrefix}{d:yyyy-MM}";
}
/// <summary>Returns [day, week, month, all-time] keys for the given instant.</summary>
public static IReadOnlyList<string> AllPeriods(DateTimeOffset when) => new[]
{
DayKey(when), WeekKey(when), MonthKey(when), AllTime,
};
}