refactor(calendar): delete JstPeriod, update doc comments

All JstPeriod callsites have moved to IGameCalendarService — delete the
static class and its tests. Updates the doc comments on Viewer.cs
(LastLoginBonusClaimedAt) and ViewerEventCounter.cs to reference the
new UTC / config-driven boundary instead of the old "JST 02:00".

GameConfigurationJsonb config-section snapshot test picks up "GameCalendar".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-04 12:00:18 -04:00
parent 448153d592
commit 6e6856db4f
5 changed files with 4 additions and 108 deletions

View File

@@ -35,7 +35,7 @@ public class Viewer : BaseEntity<long>
/// <summary> /// <summary>
/// UTC instant of the most recent daily-login-bonus claim. Null = never claimed. /// UTC instant of the most recent daily-login-bonus claim. Null = never claimed.
/// Compared against JST 02:00 day boundary via JstPeriod.DayKey to decide eligibility. /// Compared against the daily reset boundary via IGameCalendarService.ResetReady.
/// </summary> /// </summary>
public DateTime? LastLoginBonusClaimedAt { get; set; } public DateTime? LastLoginBonusClaimedAt { get; set; }

View File

@@ -3,7 +3,8 @@ namespace SVSim.Database.Models;
/// <summary> /// <summary>
/// Per-viewer "how many times has this happened" counter. Composite PK /// Per-viewer "how many times has this happened" counter. Composite PK
/// (ViewerId, EventKey, Period). Period strings: "all-time", "month:YYYY-MM", /// (ViewerId, EventKey, Period). Period strings: "all-time", "month:YYYY-MM",
/// "week:YYYY-W##", "day:YYYY-MM-DD" — all JST-anchored with 02:00 day-boundary. /// "week:YYYY-W##", "day:YYYY-MM-DD" — all UTC, with the reset-window boundary
/// set by <c>GameCalendarConfig.DailyResetUtcHour</c>.
/// Single source of truth for total_count / done_number on every wire shape. /// Single source of truth for total_count / done_number on every wire shape.
/// </summary> /// </summary>
public class ViewerEventCounter public class ViewerEventCounter

View File

@@ -1,53 +0,0 @@
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,
};
}

View File

@@ -34,7 +34,7 @@ public class GameConfigurationJsonbTests
"Player", "DefaultGrants", "DefaultLoadout", "Challenge", "Rotation", "PackRates", "Player", "DefaultGrants", "DefaultLoadout", "Challenge", "Rotation", "PackRates",
"MyRotationSchedule", "Story", "ResourceConfig", "Freeplay", "ArenaTwoPick", "Matching", "MyRotationSchedule", "Story", "ResourceConfig", "Freeplay", "ArenaTwoPick", "Matching",
"CardMasterConfig", "ColosseumSeason", "ColosseumRounds", "LoginBonus", "Guild", "CardMasterConfig", "ColosseumSeason", "ColosseumRounds", "LoginBonus", "Guild",
"SkipTutorial", "BattleXp", "SkipTutorial", "BattleXp", "GameCalendar",
})); }));
var resources = JsonSerializer.Deserialize<ResourceConfig>(byName["ResourceConfig"].ValueJson)!; var resources = JsonSerializer.Deserialize<ResourceConfig>(byName["ResourceConfig"].ValueJson)!;

View File

@@ -1,52 +0,0 @@
namespace SVSim.UnitTests.Services;
public class JstPeriodTests
{
private static DateTimeOffset Jst(int y, int m, int d, int h, int min, int s) =>
new DateTimeOffset(y, m, d, h, min, s, TimeSpan.FromHours(9));
[Test]
public void DayKey_uses_jst_with_02_00_anchor()
{
// 01:59:59 JST on May 27 belongs to the May 26 "day".
Assert.That(SVSim.EmulatedEntrypoint.Services.JstPeriod.DayKey(Jst(2026, 5, 27, 1, 59, 59)),
Is.EqualTo("day:2026-05-26"));
// 02:00:00 JST on May 27 belongs to the May 27 "day".
Assert.That(SVSim.EmulatedEntrypoint.Services.JstPeriod.DayKey(Jst(2026, 5, 27, 2, 0, 0)),
Is.EqualTo("day:2026-05-27"));
}
[Test]
public void WeekKey_is_iso8601_monday_anchored()
{
// 2026-05-25 (Monday) 02:00 JST → ISO week 2026-W22.
Assert.That(SVSim.EmulatedEntrypoint.Services.JstPeriod.WeekKey(Jst(2026, 5, 25, 2, 0, 0)),
Is.EqualTo("week:2026-W22"));
// The previous Sunday's late evening still belongs to W21 (week reset is Monday 02:00 JST).
Assert.That(SVSim.EmulatedEntrypoint.Services.JstPeriod.WeekKey(Jst(2026, 5, 25, 1, 59, 59)),
Is.EqualTo("week:2026-W21"));
}
[Test]
public void MonthKey_uses_jst_day_anchor_for_month_rollover()
{
// 01:59 JST on June 1 still belongs to May (day-boundary is 02:00 JST).
Assert.That(SVSim.EmulatedEntrypoint.Services.JstPeriod.MonthKey(Jst(2026, 6, 1, 1, 59, 0)),
Is.EqualTo("month:2026-05"));
Assert.That(SVSim.EmulatedEntrypoint.Services.JstPeriod.MonthKey(Jst(2026, 6, 1, 2, 0, 0)),
Is.EqualTo("month:2026-06"));
}
[Test]
public void AllPeriods_returns_day_week_month_all_time()
{
var t = Jst(2026, 5, 27, 12, 0, 0);
var periods = SVSim.EmulatedEntrypoint.Services.JstPeriod.AllPeriods(t);
Assert.That(periods, Is.EquivalentTo(new[] {
"day:2026-05-27", "week:2026-W22", "month:2026-05", "all-time",
}));
}
}