diff --git a/tools/legacy-db-migration/src/game.ts b/tools/legacy-db-migration/src/game.ts index 9203b17b..d897d7da 100644 --- a/tools/legacy-db-migration/src/game.ts +++ b/tools/legacy-db-migration/src/game.ts @@ -83,7 +83,16 @@ const asJsonRecord = (value: JsonValue): Record => export const resolveLegacyGameOpenedAt = (env: JsonValue, legacyDate: Date, context: string): Date => { const record = asJsonRecord(env); const candidate = record.opentime ?? record.starttime; - return candidate === null || candidate === undefined || candidate === '' ? legacyDate : toDate(candidate, context); + if (candidate === null || candidate === undefined || candidate === '') { + return legacyDate; + } + try { + return toDate(candidate, context); + } catch { + // 후기 레거시 기수는 개장 전 sentinel(-324000000/0)을 기록하기도 한다. + // raw_env는 그대로 보존하고, 유효한 ng_games.date만 파생 시각에 사용한다. + return legacyDate; + } }; const migrateSimpleTable = async ( diff --git a/tools/legacy-db-migration/test/game.test.ts b/tools/legacy-db-migration/test/game.test.ts index 96878524..79b86ef4 100644 --- a/tools/legacy-db-migration/test/game.test.ts +++ b/tools/legacy-db-migration/test/game.test.ts @@ -114,6 +114,20 @@ describe('legacy archive game migration', () => { expect(resolveLegacyGameOpenedAt({}, new Date('2020-01-01T00:00:00.000Z'), 'fixture').toISOString()).toBe( '2020-01-01T00:00:00.000Z' ); + expect( + resolveLegacyGameOpenedAt( + { opentime: -324000000, starttime: 0 }, + new Date('2026-08-10T22:00:00.000Z'), + 'fixture' + ).toISOString() + ).toBe('2026-08-10T22:00:00.000Z'); + expect( + resolveLegacyGameOpenedAt( + { opentime: 'not-a-date' }, + new Date('2026-08-10T22:00:00.000Z'), + 'fixture' + ).toISOString() + ).toBe('2026-08-10T22:00:00.000Z'); }); it('keeps dry-run target-read-only while reporting normalized formats', async () => {