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.