import { test } from 'node:test' import assert from 'node:assert/strict' import { TZ, zuUtc, ausUtc, wochentag, plusTage, heute, monatInfo, datumLang, datumKurz, uhrzeit, } from '../server/services/termine/zeit.js' import { BUNDESLAENDER, istFeiertag } from '../server/services/termine/feiertage.js' const iso = (d) => d.toISOString() test('Zeitzone ist Europe/Berlin', () => assert.equal(TZ, 'Europe/Berlin')) test('zuUtc: normale Zeit im Sommer (UTC+2) und Winter (UTC+1)', () => { assert.equal(iso(zuUtc('2026-10-12', '10:30')), '2026-10-12T08:30:00.000Z') assert.equal(iso(zuUtc('2026-01-15', '10:30')), '2026-01-15T09:30:00.000Z') }) test('zuUtc: Umstellung im Maerz, nicht existierende Zeiten sind null', () => { assert.equal(zuUtc('2026-03-29', '02:30'), null) assert.equal(zuUtc('2026-03-29', '02:00'), null) assert.equal(zuUtc('2026-03-29', '02:55'), null) assert.equal(iso(zuUtc('2026-03-29', '01:55')), '2026-03-29T00:55:00.000Z') assert.equal(iso(zuUtc('2026-03-29', '03:00')), '2026-03-29T01:00:00.000Z') }) test('zuUtc: Umstellung im Oktober, doppelte Zeit = erstes Vorkommen', () => { assert.equal(iso(zuUtc('2026-10-25', '02:30')), '2026-10-25T00:30:00.000Z') assert.equal(iso(zuUtc('2026-10-25', '02:00')), '2026-10-25T00:00:00.000Z') assert.equal(iso(zuUtc('2026-10-25', '01:55')), '2026-10-24T23:55:00.000Z') assert.equal(iso(zuUtc('2026-10-25', '03:00')), '2026-10-25T02:00:00.000Z') }) test('zuUtc: 24:00 ist der Folgetag 00:00 (auch an Umstelltagen)', () => { assert.equal(iso(zuUtc('2026-10-12', '24:00')), '2026-10-12T22:00:00.000Z') assert.equal(iso(zuUtc('2026-01-31', '24:00')), '2026-01-31T23:00:00.000Z') assert.equal(iso(zuUtc('2026-12-31', '24:00')), '2026-12-31T23:00:00.000Z') assert.equal(iso(zuUtc('2026-03-28', '24:00')), '2026-03-28T23:00:00.000Z') assert.equal(iso(zuUtc('2026-03-29', '24:00')), '2026-03-29T22:00:00.000Z') assert.equal(iso(zuUtc('2026-10-24', '24:00')), '2026-10-24T22:00:00.000Z') assert.equal(iso(zuUtc('2026-10-25', '24:00')), '2026-10-25T23:00:00.000Z') assert.deepEqual(ausUtc(zuUtc('2026-10-12', '24:00')), { tag: '2026-10-13', uhrzeit: '00:00' }) }) test('zuUtc: Tage haben 23 bzw. 25 Stunden an den Umstelltagen', () => { const stunden = (tag) => (zuUtc(plusTage(tag, 1), '00:00') - zuUtc(tag, '00:00')) / 3600000 assert.equal(stunden('2026-03-29'), 23) assert.equal(stunden('2026-10-25'), 25) assert.equal(stunden('2026-10-12'), 24) }) test('zuUtc: ungueltige Eingaben ergeben null statt Fehler', () => { for (const [tag, zeit] of [ ['2026-02-30', '10:00'], ['2026-13-01', '10:00'], ['2026-10-12', '25:00'], ['2026-10-12', '10:60'], ['2026-10-12', '24:30'], ['2026-10-12', 'abc'], ['abc', '10:00'], ['2026-10-12', '9:00'], ['2026-1-2', '10:00'], [undefined, '10:00'], ['2026-10-12', undefined], [null, null], ]) assert.equal(zuUtc(tag, zeit), null, `${tag} ${zeit}`) }) test('zuUtc: jedes 5-Minuten-Raster an den Umstelltagen hin und zurueck (nur Maerz-Luecke fehlt)', () => { for (const tag of ['2026-03-29', '2026-10-25', '2026-10-12', '2026-01-15']) { let vorher = -Infinity for (let min = 0; min < 24 * 60; min += 5) { const zeit = `${String(Math.floor(min / 60)).padStart(2, '0')}:${String(min % 60).padStart(2, '0')}` const t = zuUtc(tag, zeit) if (tag === '2026-03-29' && zeit >= '02:00' && zeit < '03:00') { assert.equal(t, null, `${tag} ${zeit}`); continue } assert.ok(t instanceof Date, `${tag} ${zeit}`) assert.deepEqual(ausUtc(t), { tag, uhrzeit: zeit }, `${tag} ${zeit}`) assert.ok(t.getTime() > vorher, `${tag} ${zeit} aufsteigend`) vorher = t.getTime() } } }) test('ausUtc rueckwaerts: Sommer, Winter, Mitternacht, Umstellung', () => { assert.deepEqual(ausUtc(new Date('2026-10-12T08:30:00Z')), { tag: '2026-10-12', uhrzeit: '10:30' }) assert.deepEqual(ausUtc(new Date('2026-01-15T09:30:00Z')), { tag: '2026-01-15', uhrzeit: '10:30' }) assert.deepEqual(ausUtc(new Date('2026-10-11T22:00:00Z')), { tag: '2026-10-12', uhrzeit: '00:00' }) assert.deepEqual(ausUtc(new Date('2026-10-11T21:59:00Z')), { tag: '2026-10-11', uhrzeit: '23:59' }) assert.deepEqual(ausUtc(new Date('2026-03-29T00:55:00Z')), { tag: '2026-03-29', uhrzeit: '01:55' }) assert.deepEqual(ausUtc(new Date('2026-03-29T01:00:00Z')), { tag: '2026-03-29', uhrzeit: '03:00' }) assert.deepEqual(ausUtc(new Date('2026-10-25T00:30:00Z')), { tag: '2026-10-25', uhrzeit: '02:30' }) assert.deepEqual(ausUtc(new Date('2026-10-25T01:30:00Z')), { tag: '2026-10-25', uhrzeit: '02:30' }) assert.deepEqual(ausUtc(new Date('2026-10-25T02:00:00Z')), { tag: '2026-10-25', uhrzeit: '03:00' }) }) test('ausUtc: Sekunden werden abgeschnitten, nie aufgerundet', () => { assert.deepEqual(ausUtc(new Date('2026-10-12T08:30:59.999Z')), { tag: '2026-10-12', uhrzeit: '10:30' }) }) test('wochentag: ISO 1..7, Montag = 1', () => { assert.equal(wochentag('2026-10-05'), 1) assert.equal(wochentag('2026-10-06'), 2) assert.equal(wochentag('2026-10-10'), 6) assert.equal(wochentag('2026-10-11'), 7) assert.equal(wochentag('2026-02-01'), 7) }) test('plusTage: Kalendertage, unabhaengig von der Zeitumstellung', () => { assert.equal(plusTage('2026-10-25', 1), '2026-10-26') assert.equal(plusTage('2026-10-24', 1), '2026-10-25') assert.equal(plusTage('2026-03-28', 1), '2026-03-29') assert.equal(plusTage('2026-03-29', 1), '2026-03-30') assert.equal(plusTage('2026-12-31', 1), '2027-01-01') assert.equal(plusTage('2026-03-01', -1), '2026-02-28') assert.equal(plusTage('2028-02-28', 1), '2028-02-29') assert.equal(plusTage('2026-10-12', 0), '2026-10-12') assert.equal(plusTage('2026-10-12', 30), '2026-11-11') }) test('heute: Berliner Kalendertag, nicht UTC-Tag', () => { assert.equal(heute(new Date('2026-10-11T22:30:00Z')), '2026-10-12') assert.equal(heute(new Date('2026-10-11T21:59:00Z')), '2026-10-11') assert.equal(heute(new Date('2026-12-31T23:30:00Z')), '2027-01-01') assert.equal(heute(new Date('2026-07-01T12:00:00Z')), '2026-07-01') }) test('monatInfo: Februar 2026 hat 28 Tage und beginnt am Sonntag', () => { assert.deepEqual(monatInfo('2026-02'), { label: 'Februar 2026', ersterWochentag: 7, anzahlTage: 28, vorher: '2026-01', nachher: '2026-03', }) }) test('monatInfo: Oktober 2026, Jahreswechsel, Schaltjahr', () => { assert.deepEqual(monatInfo('2026-10'), { label: 'Oktober 2026', ersterWochentag: 4, anzahlTage: 31, vorher: '2026-09', nachher: '2026-11', }) assert.deepEqual(monatInfo('2026-12'), { label: 'Dezember 2026', ersterWochentag: 2, anzahlTage: 31, vorher: '2026-11', nachher: '2027-01', }) assert.deepEqual(monatInfo('2027-01'), { label: 'Januar 2027', ersterWochentag: 5, anzahlTage: 31, vorher: '2026-12', nachher: '2027-02', }) assert.equal(monatInfo('2028-02').anzahlTage, 29) assert.equal(monatInfo('2026-03').label, 'März 2026') assert.equal(monatInfo('2026-04').anzahlTage, 30) }) test('monatInfo: ungueltiger Monat ergibt null', () => { for (const m of ['2026-00', '2026-13', '2026-1', '2026', 'abc', '', undefined, null, '2026-10-01']) { assert.equal(monatInfo(m), null, String(m)) } }) test('datumLang, datumKurz, uhrzeit in Berliner Wanduhr', () => { const d = new Date('2026-10-12T08:30:00Z') assert.equal(datumLang(d), 'Montag, 12.10.2026') assert.equal(datumKurz(d), '12.10.2026') assert.equal(uhrzeit(d), '10:30') const nachMitternacht = new Date('2026-10-11T22:30:00Z') assert.equal(datumLang(nachMitternacht), 'Montag, 12.10.2026') assert.equal(uhrzeit(nachMitternacht), '00:30') assert.equal(datumLang(new Date('2026-03-04T10:00:00Z')), 'Mittwoch, 04.03.2026') assert.equal(datumLang(new Date('2026-10-11T10:00:00Z')), 'Sonntag, 11.10.2026') assert.equal(datumKurz(new Date('2026-01-05T10:00:00Z')), '05.01.2026') }) test('Feiertage: 16 Bundeslaender', () => { assert.equal(BUNDESLAENDER.length, 16) assert.deepEqual([...BUNDESLAENDER].sort(), [ 'BB', 'BE', 'BW', 'BY', 'HB', 'HE', 'HH', 'MV', 'NI', 'NW', 'RP', 'SH', 'SL', 'SN', 'ST', 'TH', ]) }) test('Feiertage: regionale Unterschiede', () => { assert.equal(istFeiertag('2026-06-04', 'RP'), true) // Fronleichnam assert.equal(istFeiertag('2026-06-04', 'BE'), false) assert.equal(istFeiertag('2026-03-08', 'BE'), true) // Frauentag assert.equal(istFeiertag('2026-03-08', 'NW'), false) assert.equal(istFeiertag('2026-11-18', 'SN'), true) // Buss- und Bettag assert.equal(istFeiertag('2026-11-18', 'BY'), false) assert.equal(istFeiertag('2026-10-12', 'BY'), false) }) test('Feiertage: 25.12. und 3.10. gelten in allen Bundeslaendern', () => { for (const land of BUNDESLAENDER) { assert.equal(istFeiertag('2026-12-25', land), true, `25.12. ${land}`) assert.equal(istFeiertag('2026-10-03', land), true, `03.10. ${land}`) assert.equal(istFeiertag('2026-10-13', land), false, `13.10. ${land}`) } }) test('Feiertage: unbekanntes Land oder ungueltiger Tag ist kein Feiertag, kein Fehler', () => { assert.equal(istFeiertag('2026-12-25', 'XX'), false) assert.equal(istFeiertag('2026-12-25', 'ALL'), false) assert.equal(istFeiertag('2026-12-25', undefined), false) assert.equal(istFeiertag('2026-12-25', null), false) assert.equal(istFeiertag('foo', 'BE'), false) assert.equal(istFeiertag(undefined, 'BE'), false) })