Aspose.Email uses java.util.Date incorrectly

The Aspose getters and setters that use Java Date all do this, and ruin timestamps instead of leaving them alone.

java.util.Date is not intended to be used as a timestamp with timezone. From the JavaDoc:

Allocates a Date object and initializes it to represent the specified number of milliseconds since the standard base time known as “the epoch”, namely January 1, 1970, 00:00:00 GMT.

This drove us NUTS until we found out by peeking into the JAR that Aspose was in fact offsetting the timestamps:

    public static DateTime fromJava(Date javaDate) {
        if (javaDate == null) {
            return new DateTime();
        } else {
            long var1 = javaDate.getTime();
            long var3 = fromJavaTicks(var1 + (long)TimeZone.getDefault().getOffset(var1));
            return new DateTime(var3, 0L);
        }
    }

Someone tried to ‘fix’ it at one point (so, when you read back the Java date, the offset gets removed):

    public static Date toJava(DateTime dt) {
        if (dt == null) {
            return null;
        } else {
            long var1;
            if (MinValue.equals(dt)) {
                var1 = MinValueToUnixTicks;
            } else {
                long var3 = dt.toJavaTicks();
                var1 = var3 - (long)TimeZone.getDefault().getOffset(var3);
            }

            return new Date(var1);
        }
    }

Unfortunately they didn’t realize that sometimes it is “one-way” in the sense of e.g. exporting a PST and setting a MAPI date field, where nobody will read the timestamp back in Java for presentation. Also what happens if the JVM is in a different timezone?!?!?!?

Here is a test, albeit in Scala. It fails unless setting JVM argument -Duser.timezone=GMT (the offset is 0 in this case, which does not ruin the timestamps).

import com.aspose.email.system.DateTime

import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.Date

class AsposeEmailTimestampSpec extends AnyWordSpec with Matchers {
  "Aspose" when {
    "i pay a lot of money for a license and use timestamps correctly" should {
      "not screw up my timestamp for me anyway" in {
        val someInstant = Instant.now()
        val someInstantAsDate = Date.from(someInstant)
        // I secretly add TimeZone.getDefault().getOffset(...)
        val someInstantAsAspose = DateTime.fromJava(someInstantAsDate)

        // Your reminder to set -Duser.timezone=GMT in your JVM args
        assert(
          Instant.ofEpochMilli(someInstantAsAspose.toJavaTicks).truncatedTo(ChronoUnit.SECONDS)
            == someInstant.truncatedTo(ChronoUnit.SECONDS)
        )
      }
    }
  }
}

Please fix this immediately or at least document this magic behavior. Not cool.

Hello @dstancu,

Welcome to our support forum!
We have opened a new ticket in our internal issue tracking system:

Issue ID(s): EMAILJAVA-35275

Thank you.