| 1 | // Every surface shows the same clock: the timezone from Beheer -> Instellingen.
|
|---|
| 2 | // The Guardian PWA used to slice the raw UTC string, so a call for help sent at
|
|---|
| 3 | // 20:20 Amsterdam time read 18:20 on the dashboard.
|
|---|
| 4 | import { test } from 'node:test';
|
|---|
| 5 | import assert from 'node:assert/strict';
|
|---|
| 6 | import fs from 'fs';
|
|---|
| 7 |
|
|---|
| 8 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 9 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 10 |
|
|---|
| 11 | const dbMod = await import('../src/config/database.js');
|
|---|
| 12 | dbMod.initializeDatabase();
|
|---|
| 13 | const { formatDateTime } = await import('../src/middleware/render.js');
|
|---|
| 14 | // Through the service, not straight into the table: settings are cached, so a
|
|---|
| 15 | // raw INSERT would leave the running process on the old timezone.
|
|---|
| 16 | const { setSetting } = await import('../src/services/SettingsService.js');
|
|---|
| 17 | const setTz = (tz) => setSetting('timezone', tz);
|
|---|
| 18 |
|
|---|
| 19 | test('an AP timestamp is shown in the site timezone, not in UTC', () => {
|
|---|
| 20 | setTz('Europe/Amsterdam');
|
|---|
| 21 | const s = formatDateTime('2026-07-28T18:20:33.000Z'); // summer: UTC+2
|
|---|
| 22 | assert.match(s, /20:20/, `expected 20:20 Amsterdam time, got "${s}"`);
|
|---|
| 23 | });
|
|---|
| 24 |
|
|---|
| 25 | test('the same moment in another timezone reads differently', () => {
|
|---|
| 26 | setTz('Pacific/Auckland');
|
|---|
| 27 | assert.match(formatDateTime('2026-07-28T18:20:33.000Z'), /06:20/);
|
|---|
| 28 | setTz('UTC');
|
|---|
| 29 | assert.match(formatDateTime('2026-07-28T18:20:33.000Z'), /18:20/);
|
|---|
| 30 | });
|
|---|
| 31 |
|
|---|
| 32 | test("a SQLite timestamp is UTC even though it does not say so", () => {
|
|---|
| 33 | // CURRENT_TIMESTAMP writes "2026-07-28 18:20:33". new Date() reads a string in
|
|---|
| 34 | // that shape as LOCAL time, which is only right while the server runs on UTC.
|
|---|
| 35 | setTz('Europe/Amsterdam');
|
|---|
| 36 | const stored = formatDateTime('2026-07-28 18:20:33');
|
|---|
| 37 | const explicit = formatDateTime('2026-07-28T18:20:33.000Z');
|
|---|
| 38 | assert.equal(stored, explicit, 'a stored timestamp must not depend on the machine timezone');
|
|---|
| 39 | assert.match(stored, /20:20/);
|
|---|
| 40 | });
|
|---|
| 41 |
|
|---|
| 42 | test('a missing or unparseable timestamp renders as nothing, never as Invalid Date', () => {
|
|---|
| 43 | assert.equal(formatDateTime(null), '');
|
|---|
| 44 | assert.equal(formatDateTime(''), '');
|
|---|
| 45 | assert.equal(formatDateTime('ooit'), '');
|
|---|
| 46 | });
|
|---|
| 47 |
|
|---|
| 48 | test('the Guardian PWA gets its timestamps pre-formatted', () => {
|
|---|
| 49 | // The dashboard builds its cards in the browser and has no timezone of its
|
|---|
| 50 | // own, so the server hands over when_text alongside the raw value.
|
|---|
| 51 | const src = fs.readFileSync(new URL('../src/routes/guardian.js', import.meta.url), 'utf8');
|
|---|
| 52 | assert.match(src, /when_text: formatDateTime\(h\.published \|\| h\.created_at\)/, 'help requests');
|
|---|
| 53 | assert.match(src, /when_text: formatDateTime\(p\.published \|\| p\.created_at\)/, "the wards' feed");
|
|---|
| 54 | });
|
|---|