bugfix: date-times were displayed @ UTC, not localtime
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -7,7 +7,6 @@ import static de.srsoftware.umbrella.core.model.Translatable.t;
|
|||||||
import static de.srsoftware.umbrella.messagebus.events.Event.EventType.MEMBER_ADDED;
|
import static de.srsoftware.umbrella.messagebus.events.Event.EventType.MEMBER_ADDED;
|
||||||
|
|
||||||
import de.srsoftware.umbrella.core.constants.Field;
|
import de.srsoftware.umbrella.core.constants.Field;
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
|
||||||
import de.srsoftware.umbrella.core.model.*;
|
import de.srsoftware.umbrella.core.model.*;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import static de.srsoftware.tools.PathHandler.GET;
|
|||||||
import static de.srsoftware.tools.PathHandler.POST;
|
import static de.srsoftware.tools.PathHandler.POST;
|
||||||
import static de.srsoftware.tools.Strings.hex;
|
import static de.srsoftware.tools.Strings.hex;
|
||||||
import static de.srsoftware.umbrella.core.Errors.INVALID_URL;
|
import static de.srsoftware.umbrella.core.Errors.INVALID_URL;
|
||||||
|
import static de.srsoftware.umbrella.core.constants.Constants.TIME_FORMATTER;
|
||||||
import static de.srsoftware.umbrella.core.constants.Field.*;
|
import static de.srsoftware.umbrella.core.constants.Field.*;
|
||||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.serverError;
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.serverError;
|
||||||
import static java.lang.System.Logger.Level.*;
|
import static java.lang.System.Logger.Level.*;
|
||||||
@@ -200,7 +201,7 @@ public class Util {
|
|||||||
plantumlJar = file;
|
plantumlJar = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalDateTime dateTimeOf(long epocSecs){
|
public static String dateTimeOf(long epochMilis){
|
||||||
return LocalDateTime.ofInstant(Instant.ofEpochSecond(epocSecs), ZoneId.systemDefault());
|
return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilis), ZoneId.systemDefault()).format(TIME_FORMATTER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class Constants {
|
|||||||
public static final String NO_CACHE = "no-cache";
|
public static final String NO_CACHE = "no-cache";
|
||||||
public static final String NONE = "none";
|
public static final String NONE = "none";
|
||||||
public static final String TABLE_SETTINGS = "settings";
|
public static final String TABLE_SETTINGS = "settings";
|
||||||
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
|
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
public static final String UMBRELLA = "Umbrella";
|
public static final String UMBRELLA = "Umbrella";
|
||||||
public static final String UTF8 = UTF_8.displayName();
|
public static final String UTF8 = UTF_8.displayName();
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import static de.srsoftware.umbrella.core.model.Translatable.t;
|
|||||||
import static java.text.MessageFormat.format;
|
import static java.text.MessageFormat.format;
|
||||||
|
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -22,7 +21,6 @@ import org.json.JSONObject;
|
|||||||
public class Envelope<T extends Message<?>> {
|
public class Envelope<T extends Message<?>> {
|
||||||
private final T message;
|
private final T message;
|
||||||
private final Set<User> receivers;
|
private final Set<User> receivers;
|
||||||
private LocalDateTime time;
|
|
||||||
private final long id;
|
private final long id;
|
||||||
|
|
||||||
public Envelope(long id, T message, User receiver){
|
public Envelope(long id, T message, User receiver){
|
||||||
@@ -32,7 +30,6 @@ public class Envelope<T extends Message<?>> {
|
|||||||
public Envelope(long id, T message, Collection<? extends User> receivers) {
|
public Envelope(long id, T message, Collection<? extends User> receivers) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.receivers = new HashSet<>(receivers);
|
this.receivers = new HashSet<>(receivers);
|
||||||
time = LocalDateTime.now();
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,12 +57,12 @@ public class Envelope<T extends Message<?>> {
|
|||||||
@Override
|
@Override
|
||||||
public final boolean equals(Object o) {
|
public final boolean equals(Object o) {
|
||||||
if (!(o instanceof Envelope<?> envelope)) return false;
|
if (!(o instanceof Envelope<?> envelope)) return false;
|
||||||
return message.equals(envelope.message) && time.equals(envelope.time);
|
return message.equals(envelope.message) && id == envelope.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return 31 * message.hashCode() + time.hashCode();
|
return message.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long id(){
|
public long id(){
|
||||||
@@ -84,15 +81,6 @@ public class Envelope<T extends Message<?>> {
|
|||||||
return receivers;
|
return receivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Envelope<T> time(LocalDateTime timestamp){
|
|
||||||
this.time = timestamp;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime time(){
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return format("{0} (to: {1}), subject: {2}",getClass().getSimpleName(),receivers.stream().map(User::email).map(EmailAddress::toString).collect(Collectors.joining(", ")),message.subject());
|
return format("{0} (to: {1}), subject: {2}",getClass().getSimpleName(),receivers.stream().map(User::email).map(EmailAddress::toString).collect(Collectors.joining(", ")),message.subject());
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ public abstract class Message<T> {
|
|||||||
private final Collection<Attachment> attachments;
|
private final Collection<Attachment> attachments;
|
||||||
private final T body, subject;
|
private final T body, subject;
|
||||||
private final UmbrellaUser sender;
|
private final UmbrellaUser sender;
|
||||||
|
private long utcTime;
|
||||||
|
|
||||||
public Message(UmbrellaUser sender, T subject, T body, Collection<Attachment> attachments){
|
public Message(UmbrellaUser sender, T subject, T body, Collection<Attachment> attachments){
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.subject = subject;
|
this.subject = subject;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.attachments = attachments;
|
this.attachments = attachments;
|
||||||
|
this.utcTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Attachment> attachments(){
|
public Collection<Attachment> attachments(){
|
||||||
@@ -49,4 +51,13 @@ public abstract class Message<T> {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return format("{0}(from: {1}), subject: {2}",getClass().getSimpleName(),sender,subject);
|
return format("{0}(from: {1}), subject: {2}",getClass().getSimpleName(),sender,subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long utcTime() {
|
||||||
|
return utcTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message<T> utcTime(long newValue){
|
||||||
|
utcTime = newValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public class TranslatableMessage extends Message<Translatable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TranslatedMessage translate(String lang){
|
public TranslatedMessage translate(String lang){
|
||||||
return new TranslatedMessage(sender(),subject().translate(lang),body().translate(lang),attachments());
|
var translated = new TranslatedMessage(sender(),subject().translate(lang),body().translate(lang),attachments());
|
||||||
|
translated.utcTime(this.utcTime());
|
||||||
|
return translated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import jakarta.mail.internet.MimeMessage;
|
|||||||
import jakarta.mail.internet.MimeMultipart;
|
import jakarta.mail.internet.MimeMultipart;
|
||||||
import jakarta.mail.util.ByteArrayDataSource;
|
import jakarta.mail.util.ByteArrayDataSource;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -189,12 +191,12 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener
|
|||||||
|
|
||||||
private boolean markRead(HttpExchange ex, UmbrellaUser user, String path) {
|
private boolean markRead(HttpExchange ex, UmbrellaUser user, String path) {
|
||||||
try {
|
try {
|
||||||
var hash = Integer.parseInt(path);
|
var id = Integer.parseInt(path);
|
||||||
var envelope = queue.markRead(hash, user);
|
var envelope = queue.markRead(id, user);
|
||||||
if (envelope.isPresent()) return sendMessage(ex,user,envelope.get());
|
if (envelope.isPresent()) return sendMessage(ex,user,envelope.get());
|
||||||
return notFound(ex);
|
return notFound(ex);
|
||||||
} catch (NumberFormatException | IOException e) {
|
} catch (NumberFormatException | IOException e) {
|
||||||
throw invalidField(HASH,LONG);
|
throw invalidField(ID,LONG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,9 +223,9 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
var envelopes = queue.getEnvelopesFor(receiver);
|
var envelopes = queue.getEnvelopesFor(receiver);
|
||||||
envelopes.stream().forEach(combined::merge);
|
envelopes.stream().map(Envelope::message).forEach(combined::merge);
|
||||||
send(combined,date);
|
send(combined,date);
|
||||||
envelopes.forEach(env -> queue.markRead(env.hashCode(),receiver));
|
envelopes.forEach(env -> queue.markRead(env.id(),receiver));
|
||||||
} catch (Exception ex){
|
} catch (Exception ex){
|
||||||
LOG.log(WARNING,"Failed to deliver mail ({0}) to {1}.",combined.subject(),receiver,ex);
|
LOG.log(WARNING,"Failed to deliver mail ({0}) to {1}.",combined.subject(),receiver,ex);
|
||||||
for (var message : combined.messages()) exceptions.computeIfAbsent(new Receiver(receiver,message), k -> new ArrayList<>()).add(ex);
|
for (var message : combined.messages()) exceptions.computeIfAbsent(new Receiver(receiver,message), k -> new ArrayList<>()).add(ex);
|
||||||
@@ -328,7 +330,7 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener
|
|||||||
|
|
||||||
var sender = message.sender().name();
|
var sender = message.sender().name();
|
||||||
var subject = message.subject();
|
var subject = message.subject();
|
||||||
var time = envelope.time().format(TIME_FORMATTER);
|
var time = Instant.ofEpochMilli(message.utcTime()).atZone(ZoneId.systemDefault()).toLocalDateTime().format(TIME_FORMATTER);
|
||||||
var id = envelope.id();
|
var id = envelope.id();
|
||||||
return new JSONObject(Map.of(SENDER,sender,SUBJECT,subject,TIMESTAMP,time,ID,id));
|
return new JSONObject(Map.of(SENDER,sender,SUBJECT,subject,TIMESTAMP,time,ID,id));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,9 @@ import static de.srsoftware.umbrella.core.model.Translatable.t;
|
|||||||
import static de.srsoftware.umbrella.message.Constants.*;
|
import static de.srsoftware.umbrella.message.Constants.*;
|
||||||
import static de.srsoftware.umbrella.message.model.Schedule.schedule;
|
import static de.srsoftware.umbrella.message.model.Schedule.schedule;
|
||||||
import static java.text.MessageFormat.format;
|
import static java.text.MessageFormat.format;
|
||||||
import static java.time.ZoneOffset.UTC;
|
|
||||||
|
|
||||||
import de.srsoftware.tools.jdbc.Query;
|
import de.srsoftware.tools.jdbc.Query;
|
||||||
import de.srsoftware.umbrella.core.BaseDb;
|
import de.srsoftware.umbrella.core.BaseDb;
|
||||||
import de.srsoftware.umbrella.core.Util;
|
|
||||||
import de.srsoftware.umbrella.core.constants.Text;
|
import de.srsoftware.umbrella.core.constants.Text;
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.*;
|
import de.srsoftware.umbrella.core.model.*;
|
||||||
@@ -162,7 +160,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
|||||||
var messageId = rs.getLong(ID);
|
var messageId = rs.getLong(ID);
|
||||||
var sender = userService().loadUser(rs.getLong(SENDER_USER_ID));
|
var sender = userService().loadUser(rs.getLong(SENDER_USER_ID));
|
||||||
var msg = new TranslatedMessage(sender,rs.getString(SUBJECT),rs.getString(BODY), attachments.get(messageId));
|
var msg = new TranslatedMessage(sender,rs.getString(SUBJECT),rs.getString(BODY), attachments.get(messageId));
|
||||||
var envelope = new Envelope<>(messageId, msg, user).time(Util.dateTimeOf(rs.getLong(TIMESTAMP)));
|
var envelope = new Envelope<>(messageId, msg, user);
|
||||||
envelopes.add(envelope);
|
envelopes.add(envelope);
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
@@ -224,8 +222,8 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void push(Envelope<TranslatedMessage> envelope) {
|
public void push(Envelope<TranslatedMessage> envelope) {
|
||||||
var timestamp = envelope.time().toEpochSecond(UTC);
|
|
||||||
var message = envelope.message();
|
var message = envelope.message();
|
||||||
|
var timestamp = message.utcTime();
|
||||||
var sender = message.sender().id();
|
var sender = message.sender().id();
|
||||||
var subject = message.subject();
|
var subject = message.subject();
|
||||||
var body = message.body();
|
var body = message.body();
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
/* © SRSoftware 2025 */
|
/* © SRSoftware 2025 */
|
||||||
package de.srsoftware.umbrella.message.model;
|
package de.srsoftware.umbrella.message.model;
|
||||||
|
|
||||||
|
import static de.srsoftware.umbrella.core.Util.dateTimeOf;
|
||||||
import static java.lang.System.Logger.Level.DEBUG;
|
import static java.lang.System.Logger.Level.DEBUG;
|
||||||
import static java.lang.System.Logger.Level.TRACE;
|
import static java.lang.System.Logger.Level.TRACE;
|
||||||
import static java.text.MessageFormat.format;
|
import static java.text.MessageFormat.format;
|
||||||
|
|
||||||
import de.srsoftware.umbrella.core.model.*;
|
import de.srsoftware.umbrella.core.model.*;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class CombinedMessage {
|
public class CombinedMessage {
|
||||||
@@ -22,7 +20,6 @@ public class CombinedMessage {
|
|||||||
private final List<Message<?>> mergedMessages = new ArrayList<>();
|
private final List<Message<?>> mergedMessages = new ArrayList<>();
|
||||||
private final Translatable subjectForCombinedMessage;
|
private final Translatable subjectForCombinedMessage;
|
||||||
private UmbrellaUser sender = null;
|
private UmbrellaUser sender = null;
|
||||||
private static DateTimeFormatter DT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
||||||
|
|
||||||
public CombinedMessage(Translatable subjectForCombinedMessage, User receiver){
|
public CombinedMessage(Translatable subjectForCombinedMessage, User receiver){
|
||||||
LOG.log(DEBUG,"Creating combined message for {0}…",receiver);
|
LOG.log(DEBUG,"Creating combined message for {0}…",receiver);
|
||||||
@@ -31,9 +28,8 @@ public class CombinedMessage {
|
|||||||
this.lang = receiver.language();
|
this.lang = receiver.language();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void merge(Envelope<?> envelope) {
|
public void merge(Message<?> message) {
|
||||||
LOG.log(TRACE,"Merging {0} into combined message…",envelope);
|
LOG.log(TRACE,"Merging {0} into combined message…",message);
|
||||||
var message = envelope.message();
|
|
||||||
|
|
||||||
if (message instanceof TranslatableMessage tm) message = tm.translate(lang);
|
if (message instanceof TranslatableMessage tm) message = tm.translate(lang);
|
||||||
var body = message.body();
|
var body = message.body();
|
||||||
@@ -45,11 +41,11 @@ public class CombinedMessage {
|
|||||||
combinedSubject = subject;
|
combinedSubject = subject;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
combinedBody.insert(0,format("# {0} @ {1}\n→ {2}:\n\n",sender,envelope.time().format(DT_FORMAT),subject)); // insert sender and subject of first message right before the body of the first message
|
combinedBody.insert(0,format("# {0} @ {1}\n→ {2}:\n\n",sender, dateTimeOf(message.utcTime()),subject)); // insert sender and subject of first message right before the body of the first message
|
||||||
combinedSubject = subjectForCombinedMessage.translate(lang);
|
combinedSubject = subjectForCombinedMessage.translate(lang);
|
||||||
// no break here, we need to append the subject and content
|
// no break here, we need to append the subject and content
|
||||||
default:
|
default:
|
||||||
combinedBody.append("\n\n━━━━━━━━━━━━━━━━━━━━━\n\n# ").append(message.sender()).append(" @ ").append(envelope.time().format(DT_FORMAT)).append("\n→ ").append(subject).append(":\n\n");
|
combinedBody.append("\n\n━━━━━━━━━━━━━━━━━━━━━\n\n# ").append(message.sender()).append(" @ ").append(dateTimeOf(message.utcTime())).append("\n→ ").append(subject).append(":\n\n");
|
||||||
combinedBody.append(body);
|
combinedBody.append(body);
|
||||||
}
|
}
|
||||||
if (message.attachments() != null) attachments.addAll(message.attachments());
|
if (message.attachments() != null) attachments.addAll(message.attachments());
|
||||||
|
|||||||
Reference in New Issue
Block a user