fix(bp): convert seed JST dates to UTC for Postgres timestamp-with-tz
Npgsql rejects DateTimeOffset writes to timestamp-with-tz unless offset is zero. Caught by manual bootstrap against a real Postgres DB; SQLite test provider didn't enforce this. Converting to UTC post-parse is semantically lossless — DateTimeOffset comparisons are instant-based. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,8 +29,11 @@ public class BattlePassSeasonImporter
|
|||||||
var entry = existing.TryGetValue(s.Id, out var ex) ? ex : new BattlePassSeasonEntry { Id = s.Id };
|
var entry = existing.TryGetValue(s.Id, out var ex) ? ex : new BattlePassSeasonEntry { Id = s.Id };
|
||||||
entry.Name = s.Name;
|
entry.Name = s.Name;
|
||||||
entry.MaxLevel = s.MaxLevel;
|
entry.MaxLevel = s.MaxLevel;
|
||||||
entry.StartDate = DateTimeOffset.Parse(s.StartDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
// Postgres 'timestamp with time zone' only accepts UTC offset; JST-offset values
|
||||||
entry.EndDate = DateTimeOffset.Parse(s.EndDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
// from the seed are converted to UTC to preserve the instant. Comparisons via
|
||||||
|
// DateTimeOffset are instant-based, so the JST→UTC conversion is semantically lossless.
|
||||||
|
entry.StartDate = DateTimeOffset.Parse(s.StartDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime();
|
||||||
|
entry.EndDate = DateTimeOffset.Parse(s.EndDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime();
|
||||||
entry.CanPurchase = s.CanPurchase;
|
entry.CanPurchase = s.CanPurchase;
|
||||||
entry.PriceCrystal = s.PriceCrystal;
|
entry.PriceCrystal = s.PriceCrystal;
|
||||||
entry.Description = s.Description;
|
entry.Description = s.Description;
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ public class BattlePassSeasonImporterTests
|
|||||||
Assert.That(season.MaxLevel, Is.EqualTo(100));
|
Assert.That(season.MaxLevel, Is.EqualTo(100));
|
||||||
Assert.That(season.CanPurchase, Is.True);
|
Assert.That(season.CanPurchase, Is.True);
|
||||||
Assert.That(season.PriceCrystal, Is.EqualTo(980));
|
Assert.That(season.PriceCrystal, Is.EqualTo(980));
|
||||||
Assert.That(season.StartDate.Offset, Is.EqualTo(TimeSpan.FromHours(9)),
|
// JST-offset seed is converted to UTC for Postgres 'timestamp with time zone' compatibility.
|
||||||
"JST offset (+09:00) must round-trip through DateTimeOffset");
|
// Semantically lossless — the instant 2026-04-01T02:00+09:00 == 2026-03-31T17:00 UTC.
|
||||||
|
Assert.That(season.StartDate.Offset, Is.EqualTo(TimeSpan.Zero));
|
||||||
|
Assert.That(season.StartDate.UtcDateTime, Is.EqualTo(new DateTime(2026, 3, 31, 17, 0, 0, DateTimeKind.Utc)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
Reference in New Issue
Block a user