diff --git a/messages/de.json b/messages/de.json index 89e2eee88..4346d1d38 100644 --- a/messages/de.json +++ b/messages/de.json @@ -91,7 +91,7 @@ "colShare": "Anteil", "footerDisclaimer1": "Dieser Bericht wurde automatisch aus selbst erfassten Daten generiert und dient ausschließlich zur Unterstützung", "footerDisclaimer2": "des Arzt-Patienten-Gesprächs. Er ersetzt keine ärztliche Diagnose. Korrelationen implizieren keine Kausalität.", - "footerSource": "Quelle: HealthLog | Zeitzone: Europe/Berlin | Erstellt: {timestamp}", + "footerSource": "Quelle: HealthLog | Zeitzone: {timezone} | Erstellt: {timestamp}", "typeWeight": "Körpergewicht", "typeBpSys": "Systolischer Blutdruck", "typeBpDia": "Diastolischer Blutdruck", diff --git a/messages/en.json b/messages/en.json index e56ab4870..b0f6e29ea 100644 --- a/messages/en.json +++ b/messages/en.json @@ -91,7 +91,7 @@ "colShare": "Share", "footerDisclaimer1": "This report was generated automatically from self-tracked data and is intended to support", "footerDisclaimer2": "the doctor–patient conversation. It does not replace a medical diagnosis. Correlations do not imply causation.", - "footerSource": "Source: HealthLog | Timezone: Europe/Berlin | Created: {timestamp}", + "footerSource": "Source: HealthLog | Timezone: {timezone} | Created: {timestamp}", "typeWeight": "Body weight", "typeBpSys": "Systolic blood pressure", "typeBpDia": "Diastolic blood pressure", diff --git a/messages/es.json b/messages/es.json index cfec2263e..80274cf85 100644 --- a/messages/es.json +++ b/messages/es.json @@ -91,7 +91,7 @@ "colShare": "Porcentaje", "footerDisclaimer1": "Este informe se generó automáticamente a partir de datos autorregistrados y está destinado a apoyar", "footerDisclaimer2": "la conversación entre médico y paciente. No sustituye un diagnóstico médico. Las correlaciones no implican causalidad.", - "footerSource": "Fuente: HealthLog | Zona horaria: Europe/Berlin | Creado: {timestamp}", + "footerSource": "Fuente: HealthLog | Zona horaria: {timezone} | Creado: {timestamp}", "typeWeight": "Peso corporal", "typeBpSys": "Tensión arterial sistólica", "typeBpDia": "Tensión arterial diastólica", diff --git a/messages/fr.json b/messages/fr.json index aed77f4c2..efe4d56b1 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -91,7 +91,7 @@ "colShare": "Part", "footerDisclaimer1": "Ce rapport a été généré automatiquement à partir de données auto-déclarées et vise à soutenir", "footerDisclaimer2": "la conversation médecin-patient. Il ne remplace pas un diagnostic médical. Les corrélations n'impliquent pas la causalité.", - "footerSource": "Source : HealthLog | Fuseau horaire : Europe/Berlin | Créé : {timestamp}", + "footerSource": "Source : HealthLog | Fuseau horaire : {timezone} | Créé : {timestamp}", "typeWeight": "Poids corporel", "typeBpSys": "Tension artérielle systolique", "typeBpDia": "Tension artérielle diastolique", diff --git a/messages/it.json b/messages/it.json index b14c333b4..2c077a936 100644 --- a/messages/it.json +++ b/messages/it.json @@ -91,7 +91,7 @@ "colShare": "Quota", "footerDisclaimer1": "Questo rapporto è stato generato automaticamente da dati auto-registrati ed è destinato a supportare", "footerDisclaimer2": "il dialogo medico-paziente. Non sostituisce una diagnosi medica. Le correlazioni non implicano causalità.", - "footerSource": "Fonte: HealthLog | Fuso orario: Europe/Berlin | Creato: {timestamp}", + "footerSource": "Fonte: HealthLog | Fuso orario: {timezone} | Creato: {timestamp}", "typeWeight": "Peso corporeo", "typeBpSys": "Pressione arteriosa sistolica", "typeBpDia": "Pressione arteriosa diastolica", diff --git a/messages/pl.json b/messages/pl.json index a01a2123f..b84d2954b 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -91,7 +91,7 @@ "colShare": "Udział", "footerDisclaimer1": "Ten raport został wygenerowany automatycznie z danych samodzielnie rejestrowanych i ma na celu wspierać", "footerDisclaimer2": "rozmowę lekarza z pacjentem. Nie zastępuje diagnozy medycznej. Korelacje nie oznaczają związku przyczynowego.", - "footerSource": "Źródło: HealthLog | Strefa czasowa: Europe/Berlin | Utworzono: {timestamp}", + "footerSource": "Źródło: HealthLog | Strefa czasowa: {timezone} | Utworzono: {timestamp}", "typeWeight": "Masa ciała", "typeBpSys": "Ciśnienie skurczowe", "typeBpDia": "Ciśnienie rozkurczowe", diff --git a/src/lib/doctor-report-pdf-core.ts b/src/lib/doctor-report-pdf-core.ts index a4a06623b..ac72fd8f2 100644 --- a/src/lib/doctor-report-pdf-core.ts +++ b/src/lib/doctor-report-pdf-core.ts @@ -12,9 +12,14 @@ import { jsPDF } from "jspdf"; import autoTable from "jspdf-autotable"; import { DOCTOR_REPORT_TYPE_LABEL_KEYS } from "./doctor-report/type-label-keys"; -import { makeFormatters, type TimeFormatPreference } from "./format-locale"; +import { + makeFormatters, + DISPLAY_TIMEZONE, + type TimeFormatPreference, +} from "./format-locale"; import type { Locale } from "./i18n/config"; import { convertGlucose, resolveGlucoseUnit } from "./glucose"; +import { isValidTimezone } from "./tz/format"; import { classifyReferenceRange, formatReferenceRange, @@ -505,6 +510,13 @@ export function buildDoctorReportPdfDocument( const num = (value: number, decimals = 1) => formatters.number(value, decimals); const fmtDate = (iso: string) => formatters.date(iso); + // Same resolution `makeFormatters` applies internally (valid IANA name or + // the Europe/Berlin fallback) — the footer must state the SAME zone the + // timestamps above it were actually rendered in, not a maintainer-anchored + // default. A clinician reading a non-Berlin operator's PDF needs the real + // provenance of every timestamp on the page. + const footerTz = + userTz && isValidTimezone(userTz) ? userTz : DISPLAY_TIMEZONE; const unitFor = (type: string): string => { const staticUnit = DOCTOR_REPORT_TYPE_UNIT_KEYS[type]; @@ -1797,6 +1809,7 @@ export function buildDoctorReportPdfDocument( pageDoc.text(t("doctorReport.footerDisclaimer2"), margin, pageHeight - 10); pageDoc.text( t("doctorReport.footerSource", { + timezone: footerTz, timestamp: formatters.dateTime(now), }), margin,