Files
RSSHub/lib/utils/timezone.test.ts

92 lines
3.8 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest';
import timezone from '@/utils/timezone';
const ORIGINAL_TZ = process.env.TZ;
/**
* Re-imports the module under a given IANA zone and system time, mirroring a
* server that booted in that zone at that moment.
*/
const loadUnder = async (tz: string, now?: string) => {
process.env.TZ = tz;
if (now) {
vi.useFakeTimers();
vi.setSystemTime(new Date(now));
}
vi.resetModules();
const { default: convert } = await import('@/utils/timezone');
const { parseDate } = await import('@/utils/parse-date');
if (now) {
vi.useRealTimers();
}
return { convert, parseDate };
};
describe('timezone', () => {
afterEach(() => {
// assigning undefined would set TZ to the string "undefined", which resolves to UTC
if (ORIGINAL_TZ === undefined) {
delete process.env.TZ;
} else {
process.env.TZ = ORIGINAL_TZ;
}
vi.useRealTimers();
vi.resetModules();
});
it('timezone', () => {
const date = new Date('2024-01-01T01:01:01Z');
const serverTimezone = -date.getTimezoneOffset() / 60;
expect(timezone(date, serverTimezone - 1).toISOString()).toEqual('2024-01-01T02:01:01.000Z');
});
it('timezone with string input', () => {
const serverTimezone = -new Date('2024-01-01T01:01:01Z').getTimezoneOffset() / 60;
expect(timezone('2024-01-01T01:01:01Z', serverTimezone).toISOString()).toEqual('2024-01-01T01:01:01.000Z');
});
it('applies the offset in effect at the converted date, not the one at import time', async () => {
const { convert, parseDate } = await loadUnder('America/New_York');
// 2025-01-15 is EST (UTC-5), 2025-07-15 is EDT (UTC-4). Both wall clocks
// are declared to be UTC+0, so both must round-trip unchanged.
expect(convert(parseDate('2025-01-15 12:00:00', 'YYYY-MM-DD HH:mm:ss'), 0).toISOString()).toBe('2025-01-15T12:00:00.000Z');
expect(convert(parseDate('2025-07-15 12:00:00', 'YYYY-MM-DD HH:mm:ss'), 0).toISOString()).toBe('2025-07-15T12:00:00.000Z');
});
it('does not depend on when the process was started', async () => {
const winterBoot = await loadUnder('Europe/Berlin', '2025-01-10T00:00:00Z');
const summerBoot = await loadUnder('Europe/Berlin', '2025-07-10T00:00:00Z');
const sample = '2025-03-20 09:30:00';
const fromWinterBoot = winterBoot.convert(winterBoot.parseDate(sample, 'YYYY-MM-DD HH:mm:ss'), 8).toISOString();
const fromSummerBoot = summerBoot.convert(summerBoot.parseDate(sample, 'YYYY-MM-DD HH:mm:ss'), 8).toISOString();
expect(fromWinterBoot).toBe(fromSummerBoot);
expect(fromWinterBoot).toBe('2025-03-20T01:30:00.000Z');
});
it('is independent of the server timezone', async () => {
// sequential on purpose: `loadUnder` mutates the shared process timezone
const convertUnder = async (tz: string) => {
const { convert, parseDate } = await loadUnder(tz);
return convert(parseDate('2025-01-15 12:00:00', 'YYYY-MM-DD HH:mm:ss'), 0).toISOString();
};
expect({
UTC: await convertUnder('UTC'),
'America/New_York': await convertUnder('America/New_York'),
'Europe/Berlin': await convertUnder('Europe/Berlin'),
'Asia/Shanghai': await convertUnder('Asia/Shanghai'),
'Australia/Sydney': await convertUnder('Australia/Sydney'),
}).toEqual({
UTC: '2025-01-15T12:00:00.000Z',
'America/New_York': '2025-01-15T12:00:00.000Z',
'Europe/Berlin': '2025-01-15T12:00:00.000Z',
'Asia/Shanghai': '2025-01-15T12:00:00.000Z',
'Australia/Sydney': '2025-01-15T12:00:00.000Z',
});
});
});