Index: src/middleware/render.js
===================================================================
--- src/middleware/render.js	(revision 9effb80995066fd31d07a2d3669248119426209d)
+++ src/middleware/render.js	(revision 421c2d47017ae2891db2b8d62542cca09cf3c4ae)
@@ -31,15 +31,17 @@
 } catch { /* geen versie beschikbaar */ }
 
+// Site-tijdzone (Beheer → Instellingen). Leeg = server-default (UTC). Wordt
+// toegepast op álle server-side geformatteerde datums zodat ze in de zone van
+// de site staan i.p.v. UTC.
+const siteTimezone = () => getSetting('timezone') || undefined;
+
 const formatDate = (iso) => {
   if (!iso) return '';
-  const d = new Date(iso);
-  const months = ['januari','februari','maart','april','mei','juni','juli','augustus','september','oktober','november','december'];
-  return `${d.getDate()} ${months[d.getMonth()]} ${d.getFullYear()}`;
+  return new Date(iso).toLocaleDateString('nl-NL', { timeZone: siteTimezone(), day: 'numeric', month: 'long', year: 'numeric' });
 };
 
 const formatDateTime = (iso) => {
   if (!iso) return '';
-  const d = new Date(iso);
-  return d.toLocaleString('nl-NL', { dateStyle: 'medium', timeStyle: 'short' });
+  return new Date(iso).toLocaleString('nl-NL', { timeZone: siteTimezone(), dateStyle: 'medium', timeStyle: 'short' });
 };
 
@@ -91,4 +93,5 @@
     t: (key, vars) => i18nT(_lang, key, vars),
     langs: LANGS.map((c) => ({ code: c, name: LANG_NAMES[c], active: c === _lang })),
+    timezone: getSetting('timezone') || '',
     userOwnsSite,
     canSeeBeheer,
Index: src/routes/admin-settings.js
===================================================================
--- src/routes/admin-settings.js	(revision 9effb80995066fd31d07a2d3669248119426209d)
+++ src/routes/admin-settings.js	(revision 421c2d47017ae2891db2b8d62542cca09cf3c4ae)
@@ -116,4 +116,12 @@
       setSetting('default_lang', SUPPORTED.includes(dl) ? dl : '');
     }
+    if (typeof req.body.timezone !== 'undefined') {
+      // Site-tijdzone (IANA, bv. Europe/Amsterdam). Leeg = server-default (UTC).
+      // Valideer met Intl zodat een onzin-waarde nooit de datum-rendering breekt.
+      const tz = (req.body.timezone || '').toString().trim();
+      let valid = '';
+      if (tz) { try { Intl.DateTimeFormat('en-US', { timeZone: tz }); valid = tz; } catch { valid = ''; } }
+      setSetting('timezone', valid);
+    }
     if (typeof req.body.hub_title !== 'undefined') {
       setSetting('hub_title', (req.body.hub_title || '').toString().slice(0, 80).trim());
Index: src/services/i18n.js
===================================================================
--- src/services/i18n.js	(revision 9effb80995066fd31d07a2d3669248119426209d)
+++ src/services/i18n.js	(revision 421c2d47017ae2891db2b8d62542cca09cf3c4ae)
@@ -977,4 +977,7 @@
     'aset.default_lang_hint': 'Wat nieuwe bezoekers zien voordat ze zelf een taal kiezen. Een ingelogde gebruiker met een eigen voorkeur ziet díe.',
     'aset.default_lang_auto': 'Automatisch (browser-taal)',
+    'aset.timezone': 'Tijdzone',
+    'aset.timezone_hint': 'In welke tijdzone datums en geplande publicaties worden getoond en opgeslagen. Automatisch = serverstandaard (UTC).',
+    'aset.timezone_auto': 'Automatisch (UTC)',
   },
   en: {
@@ -1936,4 +1939,7 @@
     'aset.default_lang_hint': 'What new visitors see before they pick a language themselves. A logged-in user with their own preference sees that instead.',
     'aset.default_lang_auto': 'Automatic (browser language)',
+    'aset.timezone': 'Timezone',
+    'aset.timezone_hint': 'The timezone used to show and schedule dates and planned posts. Automatic = server default (UTC).',
+    'aset.timezone_auto': 'Automatic (UTC)',
   },
   de: {
@@ -2895,4 +2901,7 @@
     'aset.default_lang_hint': 'Was neue Besucher sehen, bevor sie selbst eine Sprache wählen. Ein angemeldeter Benutzer mit eigener Voreinstellung sieht diese.',
     'aset.default_lang_auto': 'Automatisch (Browsersprache)',
+    'aset.timezone': 'Zeitzone',
+    'aset.timezone_hint': 'Die Zeitzone, in der Datumsangaben und geplante Veröffentlichungen angezeigt und gespeichert werden. Automatisch = Server-Standard (UTC).',
+    'aset.timezone_auto': 'Automatisch (UTC)',
   },
 };
Index: src/views/pages/admin-settings.ejs
===================================================================
--- src/views/pages/admin-settings.ejs	(revision 9effb80995066fd31d07a2d3669248119426209d)
+++ src/views/pages/admin-settings.ejs	(revision 421c2d47017ae2891db2b8d62542cca09cf3c4ae)
@@ -52,4 +52,24 @@
             <option value="<%= l.code %>"<%= defaultLang === l.code ? ' selected' : '' %>><%= l.name %></option>
           <% }); %>
+        </select>
+      </label>
+      <button type="submit" class="btn btn-primary"><%= t('aset.save') %></button>
+    </form>
+  </section>
+
+  <%# ── Tijdzone ── %>
+  <section class="set-card" style="margin-top:1rem">
+    <h2><%= t('aset.timezone') %></h2>
+    <p class="set-help"><%= t('aset.timezone_hint') %></p>
+    <form method="post" action="/admin/settings" class="set-form">
+      <label class="set-field">
+        <span><%= t('aset.timezone') %></span>
+        <select name="timezone">
+          <option value=""<%= !timezone ? ' selected' : '' %>><%= t('aset.timezone_auto') %></option>
+          <%
+            var _tz = ['Europe/Amsterdam','Europe/Brussels','Europe/Berlin','Europe/Paris','Europe/London','Europe/Madrid','Europe/Rome','Europe/Lisbon','Europe/Warsaw','Europe/Athens','UTC','America/New_York','America/Chicago','America/Los_Angeles','America/Sao_Paulo','Asia/Tokyo','Asia/Dubai','Australia/Sydney'];
+            if (timezone && _tz.indexOf(timezone) < 0) _tz.unshift(timezone);
+          %>
+          <% _tz.forEach(function(z){ %><option value="<%= z %>"<%= timezone === z ? ' selected' : '' %>><%= z %></option><% }); %>
         </select>
       </label>
Index: src/views/pages/post-edit.ejs
===================================================================
--- src/views/pages/post-edit.ejs	(revision 9effb80995066fd31d07a2d3669248119426209d)
+++ src/views/pages/post-edit.ejs	(revision 421c2d47017ae2891db2b8d62542cca09cf3c4ae)
@@ -225,21 +225,37 @@
               if (!cb || !box) return;
               var inp = document.getElementById('pe-publish-at');
+              var SITE_TZ = '<%= timezone || '' %>'; // ingestelde site-tijdzone; leeg = browser-lokaal
               var pad = function (n) { return String(n).padStart(2, '0'); };
-              // Prefill: opgeslagen UTC → lokale datetime-local-waarde (zodat je je
-              // eigen tijdzone ziet, niet UTC).
-              if (inp && inp.dataset.iso) {
-                var d0 = new Date(inp.dataset.iso);
-                if (!isNaN(d0)) inp.value = d0.getFullYear() + '-' + pad(d0.getMonth() + 1) + '-' + pad(d0.getDate()) + 'T' + pad(d0.getHours()) + ':' + pad(d0.getMinutes());
+              // Offset (ms) tussen een tijdzone en UTC op een bepaald moment.
+              function tzOffset(date, tz) {
+                var f = new Intl.DateTimeFormat('en-US', { timeZone: tz, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });
+                var p = {}; f.formatToParts(date).forEach(function (x) { p[x.type] = x.value; });
+                return Date.UTC(+p.year, +p.month - 1, +p.day, +p.hour, +p.minute, +p.second) - date.getTime();
               }
-              // "Ingepland voor" in leesbare lokale tijd i.p.v. ruwe UTC-ISO.
+              // datetime-local "wandtijd" (in de site-zone) → UTC Date.
+              function wallToUtc(wall) {
+                if (!SITE_TZ) return new Date(wall);
+                var guess = new Date(wall + ':00Z').getTime();
+                return new Date(guess - tzOffset(new Date(guess), SITE_TZ));
+              }
+              // UTC-ISO → "YYYY-MM-DDTHH:MM" wandtijd in de site-zone.
+              function utcToWall(iso) {
+                var d = new Date(iso); if (isNaN(d)) return '';
+                if (!SITE_TZ) return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + 'T' + pad(d.getHours()) + ':' + pad(d.getMinutes());
+                var f = new Intl.DateTimeFormat('en-CA', { timeZone: SITE_TZ, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
+                var p = {}; f.formatToParts(d).forEach(function (x) { p[x.type] = x.value; });
+                return p.year + '-' + p.month + '-' + p.day + 'T' + p.hour + ':' + p.minute;
+              }
+              // Prefill: opgeslagen UTC → wandtijd in de site-zone.
+              if (inp && inp.dataset.iso) inp.value = utcToWall(inp.dataset.iso);
+              // "Ingepland voor" in leesbare tijd van de site-zone.
               var when = document.getElementById('pe-sched-when');
               if (when && when.dataset.iso) {
                 var dw = new Date(when.dataset.iso);
-                if (!isNaN(dw)) when.textContent = '⏳ ' + when.dataset.label + ' ' + dw.toLocaleString();
+                if (!isNaN(dw)) when.textContent = '⏳ ' + when.dataset.label + ' ' + dw.toLocaleString(undefined, SITE_TZ ? { timeZone: SITE_TZ } : undefined);
               }
               function sync() { box.style.display = cb.checked ? '' : 'none'; if (inp) inp.disabled = !cb.checked; }
               cb.addEventListener('change', sync); sync();
-              // Bij opslaan: lokale invoer → UTC-ISO via een hidden veld, zodat de
-              // server de juiste tijd opslaat (datetime-local heeft geen tijdzone).
+              // Bij opslaan: wandtijd in de site-zone → UTC-ISO via een hidden veld.
               var form = cb.closest('form');
               if (form) {
@@ -249,5 +265,5 @@
                   if (old) old.remove();
                   if (cb.checked && inp && inp.value) {
-                    var d2 = new Date(inp.value); // geïnterpreteerd in browser-tijdzone
+                    var d2 = wallToUtc(inp.value);
                     if (!isNaN(d2)) {
                       var h = document.createElement('input');
