如何确保测试总是在同一时区运行



我有一个函数,将Unix纪元时间解析为yyyy-MM-dd'T'HH:mm:ss.SSSXXX格式,以便将其导出到文件:

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);
}

现在,我对这个导出进行了测试,但是当它们在本地通过时,它们在服务器上失败了,因为它显然在不同的时区。具体来说,差异如下:

LAST-CHANGE="2017-03-13T21:36:44.261+01:00"
LAST-CHANGE="2017-03-13T20:36:44.261Z"

为了确保测试总是在同一时区运行,我已经在测试之前尝试了许多事情,例如:

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.setProperty("user.timezone", "UTC");

以及JUnitPioneer注释:

@DefaultTimeZone("UTC")

…但是,它们似乎都不影响解析输出。

我该怎么办?我想要的只是某种方法来确保我的测试在同一时区运行,而不管它们运行的机器在哪里,这样我就可以正确地测试导出。

java.time

java.utilDate-Time API及其格式化APISimpleDateFormat过时且容易出错。建议完全停止使用它们,切换到现代的Date-Time API*

您可以使用Instant.ofEpochMilli将epoch毫秒转换为Instant,然后使用Instant#toString。但是,如果它们为零,则Instant#toString省略了分数秒部分。因此,如果您需要的值严格按照模式yyyy-MM-dd'T'HH:mm:ss.SSSXXX,您可以将Instant转换为OffsetDateTime,并使用DateTimeFormatter进行格式化。

使用java.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);
}
}

输出:

2021-09-08T15:07:00Z
2021-09-08T15:07:00.000Z
<<p>

在线演示/kbd>从Trail: Date Time了解更多关于现代Date-Time API的信息.


*无论什么原因,如果你必须坚持Java 6或Java 7,你可以使用ThreeTen-Backport支持大部分java。time功能到Java 6 &7. 如果你正在为一个Android项目工作,而你的Android API级别仍然不符合Java-8,请检查Java 8+ API,通过desugaring和如何在Android项目中使用ThreeTenABP。

更好的方法是使用java.time.Instant:

Instant instant = Instant.ofEpochMilli(Long.parseLong(epochString));
return instant.toString();

无论JVM的默认时区是什么,这将始终以UTC打印。


您也可以通过在SimpleDateFormatter上设置时区来做到这一点:

REQIF_DATE_FORMAT_WITH_MILLIS.setTimeZone(TimeZone.getTimeZone("UTC"));

请注意,您需要小心使用共享SimpleDateFormat,因为它具有可变状态,这会被多个线程的访问破坏。每个线程可以有一个单独的实例,如下所示:

static final ThreadLocal<SimpleDateFormat> REQIF_DATE_FORMAT_WITH_MILLIS =
ThreadLocal.withInitial(() -> {
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf;
});

,然后使用:

return REQIF_DATE_FORMAT_WITH_MILLIS.get().format(timestamp);

但是这变得很乱,不是吗?使用Instant要容易得多。

最新更新