Files
iptvnator/libs/ui/epg/src/lib/epg-program.utils.spec.ts
T
d8d36476e6 feat(epg): add global EPG display time offset (#1489)
Adds a global EPG display-time offset (Settings → EPG, whole minutes, ±720) for guides whose provider labels programme times with the wrong timezone. Display-only: parsed XMLTV values, SQLite rows, catch-up URLs and recording snapshots keep the provider's own times, so changing it needs no guide refresh. Closes the global part of #50.

The contract lives in `libs/shared/interfaces/src/lib/epg-display-offset.util.ts` with two equivalent forms: `epgDisplayTimeMs` shifts a programme for display, `epgProviderClockMs` shifts "now" into the provider's clock for every "currently airing" decision — the batched `GET_CURRENT_PROGRAMS_BATCH` lookup takes an explicit `nowMs`, and the channel lists, the Xtream/Stalker previews, the M3U player's current-programme mirror, the unified collection resolver, the dashboard live cards and the recording overlap all pick the same programme the guide renders as "now". Portal short-EPG windows start at the provider's own "now", so under a non-zero offset the Xtream preview surfaces cut their window from the full guide at the provider clock, Stalker short-EPG requests are widened for negative offsets, and every per-stream memory of the previous offset is retired together when the setting changes.

Co-authored-by: Mark Jardine <markjardine27@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 18:46:10 +02:00

47 lines
1.4 KiB
TypeScript

import {
areProgramsSame,
deduplicateProgramsByTimeSlot,
getProgramDateKey,
getProgramTimeMs,
} from './epg-program.utils';
const PROGRAM = {
start: '2026-06-28T23:30:00.000Z',
stop: '2026-06-29T00:30:00.000Z',
channel: 'channel',
title: 'Program',
desc: null,
category: null,
};
describe('epg-program.utils', () => {
it('preserves raw time with the default offset', () => {
expect(getProgramTimeMs(PROGRAM.start)).toBe(Date.parse(PROGRAM.start));
});
it('shifts ISO and numeric timestamps in display time', () => {
expect(getProgramTimeMs(PROGRAM.start, null, 60)).toBe(
Date.parse(PROGRAM.start) + 60 * 60_000
);
expect(getProgramTimeMs(PROGRAM.start, 1_000, -30)).toBe(
1_000_000 - 30 * 60_000
);
});
it('shifts date keys across midnight', () => {
expect(getProgramDateKey(PROGRAM.start, null, 60)).toBe('2026-06-29');
});
it('uses shifted times consistently for equality and deduplication', () => {
const equivalent = {
...PROGRAM,
start: '2026-06-29T00:30:00.000Z',
stop: '2026-06-29T01:30:00.000Z',
};
expect(areProgramsSame(PROGRAM, equivalent, 60)).toBe(false);
expect(
deduplicateProgramsByTimeSlot([PROGRAM, PROGRAM], 60)
).toHaveLength(1);
});
});