Issue
I have a function that parses an Unix epoch time into the format yyyy-MM-dd'T'HH:mm:ss.SSSXXX
like this in order to export it to a file:
public static final SimpleDateFormat REQIF_DATE_FORMAT_WITH_MILLIS
= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
public static String convertEpochStringToReqifDateString(String epochString) {
Date timestamp = new Date(Long.parseLong(epochString));
return REQIF_DATE_FORMAT_WITH_MILLIS.format(timestamp);
}
Now, I have tests for this export, but while they pass locally, they fail on the server because it's apparently in a different time zone. Specifically, the differences look like this:
LAST-CHANGE="2017-03-13T21:36:44.261+01:00"
LAST-CHANGE="2017-03-13T20:36:44.261Z"
I have already tried running a number of things before the test in order to make sure that the test is always run in the same time zone, such as:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.setProperty("user.timezone", "UTC");
As well as the JUnitPioneer annotation:
@DefaultTimeZone("UTC")
...however, none of them seemed to affect the parsing output at all.
What can I do about this? All I want is some way to ensure that my tests are run in the same time zone regardless of where the machine they are run is standing so I can test the export correctly.
Solution
java.time
The java.util
Date-Time API and their formatting API, SimpleDateFormat
are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
You can use Instant.ofEpochMilli
to convert the epoch milliseconds into Instant
and then use the Instant#toString
. However, Instant#toString
omits the fraction-of-second part if they are zero. Therefore, if you need the value strictly in the pattern, yyyy-MM-dd'T'HH:mm:ss.SSSXXX
, you can convert the Instant
to OffsetDateTime
and format it using a DateTimeFormatter
.
Solution using java.time
, the modern Date-Time API:
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// An example epoch milliseconds
long millis = 1631113620000L;
Instant instant = Instant.ofEpochMilli(millis);
String strDateTime = instant.toString();
System.out.println(strDateTime);
// If you need the value strictly in the pattern, yyyy-MM-dd'T'HH:mm:ss.SSSXXX
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
OffsetDateTime odt = instant.atOffset(ZoneOffset.UTC);
strDateTime = odt.format(dtf);
System.out.println(strDateTime);
}
}
Output:
2021-09-08T15:07:00Z
2021-09-08T15:07:00.000Z
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
Answered By - Arvind Kumar Avinash
Answer Checked By - David Goodson (JavaFixing Volunteer)