From 03ff3795b29f4fed831bb6695db2bf496923d297 Mon Sep 17 00:00:00 2001 From: sylyx Date: Fri, 21 Aug 2026 10:54:22 +0200 Subject: [PATCH] Datum + Uhrzeit: Uhrzeit ging in Text- und DateTime-Feldern verloren Das Token "Datum + Uhrzeit der E-Mail" lieferte nur das Datum. Drei Ursachen: 1. setFieldValue schrieb ein Date in ein TEXT-Feld ueber toDateInput, also nur "2026-08-04" - die Uhrzeit war weg, obwohl ein Textfeld sie problemlos traegt. Beim Upload wurde daraus String(dateObjekt), also die JS-Standardausgabe "Mon Aug 04 2026 16:28:51 GMT+0200 (...)". Neu: DocuWare.dateToText() liefert "04.08.2026 16:28:51", bei exakter Mitternacht nur "04.08.2026". Gilt fuer Text-, Memo- und Keyword-Felder und fuer Anzeige wie Upload gleichermassen. 2. TOKEN_FN.date war identisch mit dateTime (beide m.date). In einem Datums- oder DateTime-Feld fiel das nicht auf, in einem Textfeld schon: "Datum" haette dort ebenfalls eine Uhrzeit getragen. date liefert jetzt via dayOnly() den Kalendertag (lokale Mitternacht), wie archiveDate es schon tat. 3. Das datetime-local-Control kannte ohne step="1" nur Minuten und schnitt die Sekunden ab; fmtTime liefert sie jetzt mit. Ausserdem geklaert: DateTime geht als LOKALE Wanduhrzeit ohne Zeitzonen-Suffix raus ("2026-08-04T16:28:51") statt als toISOString() mit "Z". Ob der Server das "Z" zurueckrechnet, haengt von dessen Zeitzone ab (Cloud meist UTC) - Zeiten erschienen verschoben, und lokale Mitternacht kippte als UTC auf den VORTAG 22:00. Ohne Suffix ist der Wert eindeutig. Date-Felder bleiben unveraendert. Co-Authored-By: Claude Opus 5 --- dialog/dialog.js | 27 +++++++++++++++++++++++---- lib/docuware.js | 47 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/dialog/dialog.js b/dialog/dialog.js index f95ec0d..c910550 100644 --- a/dialog/dialog.js +++ b/dialog/dialog.js @@ -37,7 +37,10 @@ const TOKEN_FN = { bcc: (m) => m.bcc, subject: (m) => m.subject, body: (m) => m.bodyText, - date: (m) => m.date, + // Nur der Kalendertag: auf lokale Mitternacht gesetzt, damit „Datum" auch in + // einem DateTime- oder Textfeld keine Uhrzeit trägt (gleiche Konvention wie + // archiveDate). Den echten Zeitpunkt liefert dateTime. + date: (m) => dayOnly(m.date), direction: (m) => m.direction, size: (m) => m.sizeBytes, account: (m) => m.account, @@ -49,7 +52,7 @@ const TOKEN_FN = { time: (m) => fmtTime(m.date), // archiveDate ist bewusst auf Mitternacht gesetzt: so bleibt es „nur Datum", // auch wenn es auf ein DateTime-Feld gemappt wird. archiveDateTime trägt die Uhrzeit. - archiveDate: () => { const n = new Date(); return new Date(n.getFullYear(), n.getMonth(), n.getDate()); }, + archiveDate: () => dayOnly(new Date()), archiveDateTime: () => new Date(), // Dokumentbezogen @@ -500,6 +503,8 @@ function buildInput(f) { // DateTime-Feld: Tag UND Uhrzeit erfassbar (vor dem reinen date-Zweig prüfen). el = document.createElement("input"); el.type = "datetime-local"; + // Ohne step="1" kennt das Control nur Minuten und schneidet die Sekunden ab. + el.step = "1"; } else if (t.includes("date")) { el = document.createElement("input"); el.type = "date"; @@ -564,7 +569,10 @@ function setFieldValue(name, f, val) { } el.value = String(val); } else { - el.value = val instanceof Date ? toDateInput(val) : String(val); + // Textfeld: Datum lesbar ausschreiben ("04.08.2026 16:28:51"). Früher lief + // hier toDateInput – das warf die Uhrzeit weg, obwohl ein Textfeld sie + // problemlos trägt (genau der Fall „Datum + Uhrzeit" ohne Uhrzeit). + el.value = val instanceof Date ? DocuWare.dateToText(val) : String(val); } wrap.classList.add("prefilled"); } @@ -859,10 +867,21 @@ const toDateInput = (d) => { // Lokal als YYYY-MM-DDTHH:MM für . const toDateTimeInput = (d) => `${toDateInput(d)}T${fmtTime(d)}`; // Uhrzeit HH:MM lokal (auch als eigenständiger Text-Token nutzbar). +// Kalendertag ohne Uhrzeit (lokale Mitternacht). Funktionsdeklaration, damit +// TOKEN_FN sie oben im Modul schon verwenden kann. +function dayOnly(d) { + const dt = d instanceof Date ? d : new Date(d); + if (isNaN(dt)) return dt; + return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()); +} + +// Uhrzeit inkl. Sekunden ("16:28:51"). Die Sekunden braucht auch +// toDateTimeInput – ein datetime-local mit step="1" nimmt sie entgegen. function fmtTime(d) { const dt = d instanceof Date ? d : new Date(d); if (isNaN(dt)) return ""; - return `${String(dt.getHours()).padStart(2, "0")}:${String(dt.getMinutes()).padStart(2, "0")}`; + const p = (n) => String(n).padStart(2, "0"); + return `${p(dt.getHours())}:${p(dt.getMinutes())}:${p(dt.getSeconds())}`; } // Dateiname, den die E-Mail selbst beim Upload bekäme – für die Vorschau im diff --git a/lib/docuware.js b/lib/docuware.js index da87194..802fce7 100644 --- a/lib/docuware.js +++ b/lib/docuware.js @@ -427,22 +427,47 @@ const DocuWare = { return out; }, + /** + * Date-Objekt als deutscher Text: "04.08.2026 16:28:51". Ohne Uhrzeit (exakt + * Mitternacht) nur "04.08.2026" – so tragen reine Datums-Tokens kein + * künstliches " 00:00:00". Gebraucht, sobald ein Datum in einem TEXT-Feld + * landet: String(dateObjekt) lieferte sonst die JS-Standardausgabe + * ("Mon Aug 04 2026 16:28:51 GMT+0200 (Mitteleuropäische Sommerzeit)"). + * Bewusst lokale Zeit ohne UTC-Umrechnung – der Text soll zeigen, was der + * Nutzer in Thunderbird sieht. + */ + dateToText(value) { + const d = value instanceof Date ? value : new Date(value); + if (isNaN(d)) return String(value); + const p = (n) => String(n).padStart(2, "0"); + const day = `${p(d.getDate())}.${p(d.getMonth() + 1)}.${d.getFullYear()}`; + if (!d.getHours() && !d.getMinutes() && !d.getSeconds()) return day; + return `${day} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`; + }, + _field(name, value, type) { const t = (type || "Text").toLowerCase(); + // Datum in einem Text-/Memo-Feld lesbar machen (s. dateToText). + const asText = (v) => (v instanceof Date ? this.dateToText(v) : String(v)); // WICHTIG: "datetime" zuerst prüfen – "DateTime".includes("date") ist true und // würde sonst die Uhrzeit auf Mitternacht normalisieren (Datenverlust). if (t.includes("datetime")) { const d = value instanceof Date ? value : new Date(value); if (isNaN(d)) return { FieldName: name, Item: String(value), ItemElementName: "DateTime" }; - // Mit Uhrzeit: echter Zeitpunkt, daher unverändert als ISO (UTC) senden. + // LOKALE Wanduhrzeit ohne Zeitzonen-Suffix ("2026-08-04T16:28:51"). // - // UNGEPRÜFT gegen den Server: ob DocuWare das "Z" auswertet (dann stimmt die - // Uhrzeit) oder die ISO-Zeichenkette als lokale Wanduhrzeit übernimmt (dann - // erscheint sie um den UTC-Versatz verschoben, in Wien -2 h). Reine Date-Felder - // sind davon nicht betroffen, die werden auf UTC-Mitternacht normalisiert. - // Falls im Testarchiv verschoben: hier stattdessen die lokale Wanduhrzeit ohne - // Zeitzone senden, also `${toLocalIso(d)}` statt d.toISOString(). - return { FieldName: name, Item: d.toISOString(), ItemElementName: "DateTime" }; + // Vorher ging hier d.toISOString() raus, also UTC mit "Z". Ob DocuWare das + // "Z" in Serverzeit zurückrechnet, hängt vom Server ab (Cloud-Instanzen + // laufen meist auf UTC) – die Uhrzeit erschien dann um den Versatz + // verschoben, in Wien -2 h. Schlimmer beim Kalendertag: lokale Mitternacht + // wird als UTC zum VORTAG 22:00. + // Ohne Suffix ist der Wert eindeutig: der Server übernimmt ihn so, wie der + // Nutzer die Zeit in Thunderbird sieht. Reine Date-Felder bleiben unberührt. + const p = (n) => String(n).padStart(2, "0"); + const iso = + `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}` + + `T${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`; + return { FieldName: name, Item: iso, ItemElementName: "DateTime" }; } if (t.includes("date")) { // DocuWare erwartet /Date(ms)/ oder ISO; ISO funktioniert bei Platform. @@ -461,13 +486,13 @@ const DocuWare = { return { FieldName: name, Item: Number(value), ItemElementName: "Int" }; } if (t.includes("memo")) { - return { FieldName: name, Item: String(value), ItemElementName: "Memo" }; + return { FieldName: name, Item: asText(value), ItemElementName: "Memo" }; } if (t.includes("keyword")) { - const arr = Array.isArray(value) ? value : [String(value)]; + const arr = Array.isArray(value) ? value : [asText(value)]; return { FieldName: name, Item: arr, ItemElementName: "Keywords" }; } - return { FieldName: name, Item: String(value), ItemElementName: "String" }; + return { FieldName: name, Item: asText(value), ItemElementName: "String" }; }, _isSystemField(dbName) {