Repository: javacord/javacord
Issue: 'Timestamp' attribute on messages ignores the timezone.

Currently the timestamp attribute on messages completely ignores the timezone, which causes major issues when comparing the time. As no TZ is supplied, Java assumes it wants to use the system time, which means unless the system is set to use UTC, the time will be wrong.

This can be remedied in one of two ways:
- Just set the calendar instance to UTC
- Or better, implement proper timestamp handling that includes the timezone.


Changed files:
src/main/java/de/btobastian/javacord/entities/message/impl/ImplMessage.java

diff --git a/src/main/java/de/btobastian/javacord/entities/message/impl/ImplMessage.java b/src/main/java/de/btobastian/javacord/entities/message/impl/ImplMessage.java
index 07c667ff..184dc29a 100644
--- a/src/main/java/de/btobastian/javacord/entities/message/impl/ImplMessage.java
+++ b/src/main/java/de/btobastian/javacord/entities/message/impl/ImplMessage.java
@@ -18,6 +18,7 @@
  */
 package de.btobastian.javacord.entities.message.impl;
 
+import com.google.common.base.Joiner;
 import com.google.common.util.concurrent.FutureCallback;
 import com.mashape.unirest.http.HttpResponse;
 import com.mashape.unirest.http.JsonNode;
@@ -59,9 +60,30 @@ public class ImplMessage implements Message {
      */
     private static final Logger logger = LoggerUtil.getLogger(ImplMessage.class);
 
-    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
-    private static final SimpleDateFormat FORMAT_ALTERNATIVE = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
-    private static final SimpleDateFormat FORMAT_ALTERNATIVE_TWO = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");
+    private static final ThreadLocal<SimpleDateFormat> TIMEZONE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
+        }
+    };
+    private static final ThreadLocal<SimpleDateFormat> FORMAT = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
+        }
+    };
+    private static final ThreadLocal<SimpleDateFormat> FORMAT_ALTERNATIVE = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+        }
+    };
+    private static final ThreadLocal<SimpleDateFormat> FORMAT_ALTERNATIVE_TWO = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");
+        }
+    };
 
     private final ImplDiscordAPI api;
 
@@ -93,15 +115,19 @@ public class ImplMessage implements Message {
         if (data.has("timestamp")) {
             String time = data.getString("timestamp");
             Calendar calendar = Calendar.getInstance();
-            synchronized (FORMAT) { // SimpleDateFormat#parse() isn't thread safe...
-                try {
-                    calendar.setTime(FORMAT.parse(time.substring(0, time.length() - 9)));
+            try {
+                //remove the nano seconds, rejoining on +. If the formatting changes then the string will remain the same
+                String nanoSecondsRemoved = Joiner.on("+").join(time.split("\\\\d{3}\\\\+"));
+                calendar.setTime(TIMEZONE_FORMAT.get().parse(nanoSecondsRemoved));
+            } catch (ParseException timeZoneIgnored) {
+                try { //Continuing with previous code before Issue 15 fix
+                    calendar.setTime(FORMAT.get().parse(time.substring(0, time.length() - 9)));
                 } catch (ParseException ignored) {
                     try {
-                        calendar.setTime(FORMAT_ALTERNATIVE.parse(time.substring(0, time.length() - 9)));
+                        calendar.setTime(FORMAT_ALTERNATIVE.get().parse(time.substring(0, time.length() - 9)));
                     } catch (ParseException ignored2) {
                         try {
-                            calendar.setTime(FORMAT_ALTERNATIVE_TWO.parse(time.substring(0, time.length() - 9)));
+                            calendar.setTime(FORMAT_ALTERNATIVE_TWO.get().parse(time.substring(0, time.length() - 9)));
                         } catch (ParseException e) {
                             logger.warn("Could not parse timestamp {}. Please contact the developer!", time, e);
                         }