如何确保测试始终在同一时区运行
How to make sure test always runs in the same time zone
我有一个函数可以将 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")
...然而,其中 none 似乎完全影响了解析输出。
我该怎么办?我想要的只是某种方法来确保我的测试 运行 在同一时区,而不管它们 运行 所在的机器在哪里,这样我就可以正确测试导出。
更好的方法是使用 java.time.Instant
:
Instant instant = Instant.ofEpochMilli(Long.parseLong(epochString));
return instant.toString();
这将始终以 UTC 打印,无论 JVM 的默认时区如何。
您也可以通过 setting the time zone on the 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
.
更容易
java.time
java.util
日期时间 API 及其格式 API、SimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*.
您可以使用 Instant.ofEpochMilli
to convert the epoch milliseconds into Instant
and then use the Instant#toString
. However, 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
了解有关现代日期时间 API 的更多信息
* 无论出于何种原因,如果您必须坚持Java 6 或Java 7,您可以使用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 。
我有一个函数可以将 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")
...然而,其中 none 似乎完全影响了解析输出。
我该怎么办?我想要的只是某种方法来确保我的测试 运行 在同一时区,而不管它们 运行 所在的机器在哪里,这样我就可以正确测试导出。
更好的方法是使用 java.time.Instant
:
Instant instant = Instant.ofEpochMilli(Long.parseLong(epochString));
return instant.toString();
这将始终以 UTC 打印,无论 JVM 的默认时区如何。
您也可以通过 setting the time zone on the 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
.
java.time
java.util
日期时间 API 及其格式 API、SimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*.
您可以使用 Instant.ofEpochMilli
to convert the epoch milliseconds into Instant
and then use the Instant#toString
. However, 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
了解有关现代日期时间 API 的更多信息
* 无论出于何种原因,如果您必须坚持Java 6 或Java 7,您可以使用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