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:
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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)!;
|
||||||
|
|||||||
@@ -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",
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user