fix: correct timezone display in messages — parse server UTC dates properly
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Server stores timestamps in UTC without timezone suffix. JavaScript new Date() treated them as local time, showing times 2h behind. Added parseUTC() helper that appends 'Z' to naive ISO dates so the browser correctly converts UTC → user's local timezone. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
898c10921d
commit
f0bdfe013b
@ -84,9 +84,18 @@
|
|||||||
d.getFullYear() === y.getFullYear();
|
d.getFullYear() === y.getFullYear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server stores UTC but isoformat() omits timezone — append Z so JS knows it's UTC
|
||||||
|
function parseUTC(dateStr) {
|
||||||
|
if (!dateStr) return null;
|
||||||
|
if (dateStr.indexOf('Z') === -1 && dateStr.indexOf('+') === -1 && dateStr.indexOf('T') !== -1) {
|
||||||
|
return new Date(dateStr + 'Z');
|
||||||
|
}
|
||||||
|
return new Date(dateStr);
|
||||||
|
}
|
||||||
|
|
||||||
function formatTime(dateStr) {
|
function formatTime(dateStr) {
|
||||||
if (!dateStr) return '';
|
if (!dateStr) return '';
|
||||||
var d = new Date(dateStr);
|
var d = parseUTC(dateStr);
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
var diff = (now - d) / 1000;
|
var diff = (now - d) / 1000;
|
||||||
if (diff < 60) return 'teraz';
|
if (diff < 60) return 'teraz';
|
||||||
@ -98,12 +107,12 @@
|
|||||||
|
|
||||||
function formatMessageTime(dateStr) {
|
function formatMessageTime(dateStr) {
|
||||||
if (!dateStr) return '';
|
if (!dateStr) return '';
|
||||||
var d = new Date(dateStr);
|
var d = parseUTC(dateStr);
|
||||||
return d.toLocaleTimeString('pl', { hour: '2-digit', minute: '2-digit' });
|
return d.toLocaleTimeString('pl', { hour: '2-digit', minute: '2-digit' });
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDateSeparator(dateStr) {
|
function formatDateSeparator(dateStr) {
|
||||||
var d = new Date(dateStr);
|
var d = parseUTC(dateStr);
|
||||||
if (isToday(d)) return 'Dzisiaj';
|
if (isToday(d)) return 'Dzisiaj';
|
||||||
if (isYesterday(d)) return 'Wczoraj';
|
if (isYesterday(d)) return 'Wczoraj';
|
||||||
return d.toLocaleDateString('pl', { day: 'numeric', month: 'long', year: 'numeric' });
|
return d.toLocaleDateString('pl', { day: 'numeric', month: 'long', year: 'numeric' });
|
||||||
@ -111,7 +120,7 @@
|
|||||||
|
|
||||||
function formatPresence(lastSeen) {
|
function formatPresence(lastSeen) {
|
||||||
if (!lastSeen) return '';
|
if (!lastSeen) return '';
|
||||||
var d = new Date(lastSeen);
|
var d = parseUTC(lastSeen);
|
||||||
return 'ostatnio: ' + d.toLocaleDateString('pl', { day: '2-digit', month: '2-digit' }) +
|
return 'ostatnio: ' + d.toLocaleDateString('pl', { day: '2-digit', month: '2-digit' }) +
|
||||||
' o ' + d.toLocaleTimeString('pl', { hour: '2-digit', minute: '2-digit' });
|
' o ' + d.toLocaleTimeString('pl', { hour: '2-digit', minute: '2-digit' });
|
||||||
}
|
}
|
||||||
@ -329,7 +338,7 @@
|
|||||||
state.conversations.sort(function (a, b) {
|
state.conversations.sort(function (a, b) {
|
||||||
var aTime = a.last_message ? a.last_message.created_at : a.updated_at;
|
var aTime = a.last_message ? a.last_message.created_at : a.updated_at;
|
||||||
var bTime = b.last_message ? b.last_message.created_at : b.updated_at;
|
var bTime = b.last_message ? b.last_message.created_at : b.updated_at;
|
||||||
return new Date(bTime || 0) - new Date(aTime || 0);
|
return (parseUTC(bTime) || 0) - (parseUTC(aTime) || 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
ConversationList.renderList();
|
ConversationList.renderList();
|
||||||
@ -479,7 +488,7 @@
|
|||||||
messages.forEach(function (msg) {
|
messages.forEach(function (msg) {
|
||||||
// Date separator
|
// Date separator
|
||||||
if (msg.created_at) {
|
if (msg.created_at) {
|
||||||
var msgDate = new Date(msg.created_at).toDateString();
|
var msgDate = parseUTC(msg.created_at).toDateString();
|
||||||
if (msgDate !== lastDate) {
|
if (msgDate !== lastDate) {
|
||||||
var sep = el('div', 'date-separator');
|
var sep = el('div', 'date-separator');
|
||||||
sep.textContent = formatDateSeparator(msg.created_at);
|
sep.textContent = formatDateSeparator(msg.created_at);
|
||||||
@ -641,18 +650,18 @@
|
|||||||
var readAt = null;
|
var readAt = null;
|
||||||
|
|
||||||
if (details && details.members) {
|
if (details && details.members) {
|
||||||
var msgTime = new Date(msg.created_at);
|
var msgTime = parseUTC(msg.created_at);
|
||||||
var otherMembers = details.members.filter(function (m) {
|
var otherMembers = details.members.filter(function (m) {
|
||||||
return m.user_id !== window.__CURRENT_USER__.id;
|
return m.user_id !== window.__CURRENT_USER__.id;
|
||||||
});
|
});
|
||||||
isRead = otherMembers.length > 0 && otherMembers.every(function (m) {
|
isRead = otherMembers.length > 0 && otherMembers.every(function (m) {
|
||||||
return m.last_read_at && new Date(m.last_read_at) >= msgTime;
|
return m.last_read_at && parseUTC(m.last_read_at) >= msgTime;
|
||||||
});
|
});
|
||||||
if (isRead) {
|
if (isRead) {
|
||||||
// Find earliest read time among others
|
// Find earliest read time among others
|
||||||
var readTimes = otherMembers
|
var readTimes = otherMembers
|
||||||
.filter(function (m) { return m.last_read_at; })
|
.filter(function (m) { return m.last_read_at; })
|
||||||
.map(function (m) { return new Date(m.last_read_at); });
|
.map(function (m) { return parseUTC(m.last_read_at); });
|
||||||
if (readTimes.length) {
|
if (readTimes.length) {
|
||||||
readAt = new Date(Math.min.apply(null, readTimes));
|
readAt = new Date(Math.min.apply(null, readTimes));
|
||||||
}
|
}
|
||||||
@ -670,18 +679,18 @@
|
|||||||
// Group: show per-member read status
|
// Group: show per-member read status
|
||||||
// Sort: read (earliest first), then unread (alphabetically)
|
// Sort: read (earliest first), then unread (alphabetically)
|
||||||
otherMembers.sort(function (a, b) {
|
otherMembers.sort(function (a, b) {
|
||||||
var aRead = a.last_read_at && new Date(a.last_read_at) >= msgTime;
|
var aRead = a.last_read_at && parseUTC(a.last_read_at) >= msgTime;
|
||||||
var bRead = b.last_read_at && new Date(b.last_read_at) >= msgTime;
|
var bRead = b.last_read_at && parseUTC(b.last_read_at) >= msgTime;
|
||||||
if (aRead && !bRead) return -1;
|
if (aRead && !bRead) return -1;
|
||||||
if (!aRead && bRead) return 1;
|
if (!aRead && bRead) return 1;
|
||||||
if (aRead && bRead) return new Date(a.last_read_at) - new Date(b.last_read_at);
|
if (aRead && bRead) return parseUTC(a.last_read_at) - parseUTC(b.last_read_at);
|
||||||
return (a.name || '').localeCompare(b.name || '', 'pl');
|
return (a.name || '').localeCompare(b.name || '', 'pl');
|
||||||
});
|
});
|
||||||
var lines = [];
|
var lines = [];
|
||||||
otherMembers.forEach(function (m) {
|
otherMembers.forEach(function (m) {
|
||||||
var name = m.name || 'Użytkownik';
|
var name = m.name || 'Użytkownik';
|
||||||
if (m.last_read_at && new Date(m.last_read_at) >= msgTime) {
|
if (m.last_read_at && parseUTC(m.last_read_at) >= msgTime) {
|
||||||
var d = new Date(m.last_read_at);
|
var d = parseUTC(m.last_read_at);
|
||||||
var dateStr = d.toLocaleDateString('pl', {day:'2-digit', month:'2-digit'}) + ' o ' + d.toLocaleTimeString('pl', {hour:'2-digit', minute:'2-digit'});
|
var dateStr = d.toLocaleDateString('pl', {day:'2-digit', month:'2-digit'}) + ' o ' + d.toLocaleTimeString('pl', {hour:'2-digit', minute:'2-digit'});
|
||||||
lines.push('<span class="read-status-line read">✓ ' + name + ' — ' + dateStr + '</span>');
|
lines.push('<span class="read-status-line read">✓ ' + name + ' — ' + dateStr + '</span>');
|
||||||
} else {
|
} else {
|
||||||
@ -778,11 +787,11 @@
|
|||||||
if (lastRow) {
|
if (lastRow) {
|
||||||
var lastMsg = state.messages[convId][state.messages[convId].length - 2];
|
var lastMsg = state.messages[convId][state.messages[convId].length - 2];
|
||||||
if (lastMsg && lastMsg.created_at) {
|
if (lastMsg && lastMsg.created_at) {
|
||||||
lastDate = new Date(lastMsg.created_at).toDateString();
|
lastDate = parseUTC(lastMsg.created_at).toDateString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msg.created_at) {
|
if (msg.created_at) {
|
||||||
var msgDate = new Date(msg.created_at).toDateString();
|
var msgDate = parseUTC(msg.created_at).toDateString();
|
||||||
if (msgDate !== lastDate) {
|
if (msgDate !== lastDate) {
|
||||||
var sep = el('div', 'date-separator');
|
var sep = el('div', 'date-separator');
|
||||||
sep.textContent = formatDateSeparator(msg.created_at);
|
sep.textContent = formatDateSeparator(msg.created_at);
|
||||||
@ -890,7 +899,7 @@
|
|||||||
|
|
||||||
var isMine = msg.sender && msg.sender.id === window.__CURRENT_USER__.id;
|
var isMine = msg.sender && msg.sender.id === window.__CURRENT_USER__.id;
|
||||||
var canEdit = isMine && msg.created_at &&
|
var canEdit = isMine && msg.created_at &&
|
||||||
(new Date() - new Date(msg.created_at)) < 24 * 60 * 60 * 1000;
|
(new Date() - parseUTC(msg.created_at)) < 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
// Show/hide owner-only buttons
|
// Show/hide owner-only buttons
|
||||||
menu.querySelectorAll('.owner-only').forEach(function (btn) {
|
menu.querySelectorAll('.owner-only').forEach(function (btn) {
|
||||||
|
|||||||
@ -326,7 +326,7 @@ window.__CSRF_TOKEN__ = '{{ csrf_token() }}';
|
|||||||
// Load conversations.js after data is set
|
// Load conversations.js after data is set
|
||||||
(function() {
|
(function() {
|
||||||
var s = document.createElement('script');
|
var s = document.createElement('script');
|
||||||
s.src = '{{ url_for("static", filename="js/conversations.js") }}?v=24';
|
s.src = '{{ url_for("static", filename="js/conversations.js") }}?v=25';
|
||||||
document.body.appendChild(s);
|
document.body.appendChild(s);
|
||||||
})();
|
})();
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user