You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java DateTime转换单元测试本地正常但远程流水线执行失败求助

问题:Quarkus应用时区转换测试本地正常,流水线执行失败

我有一个Quarkus应用,需要将带GMT偏移的字符串时间戳转换为Europe/Berlin时区时间,例如时间戳:"2022-08-13 10:13:56.48382+03"。相关单元测试在本地运行完全正常,但在远程流水线中执行失败。我已在应用启动时执行TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));设置默认时区,且转换逻辑中已显式指定时区,但怀疑流水线服务器的本地时区仍对测试产生影响,同时正在考虑修改流水线时区来解决问题,请问测试失败的原因是什么?

时间转换逻辑

public static Date convertTime(String timestamp) {

    // offset being f.e. +01, +12, -03
    String gmtZone = "GMT" + getOffset(timestamp);
    // removes the milliseconds from time
    timestamp = stripMilliseconds(timestamp);

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern(INCOMING_TIMESTAMP_FORMAT);
    LocalDateTime localDateTime = LocalDateTime.parse(timestamp, dtf);
    ZonedDateTime zdtGmt = ZonedDateTime.of(localDateTime, ZoneId.of(gmtZone));
    ZonedDateTime zdtBerlin = zdtGmt.withZoneSameInstant(ZoneId.of(TIME_ZONE_EUROPE_BERLIN));

    return Date.from(zdtBerlin.toInstant());
}

测试代码

@ParameterizedTest
@MethodSource("timestampsWithOffsetWithCorrectlyConvertedTime")
void offsetShouldBeCorrectlyConvertedToBerlinTime(String timestamp, Date expectedConvertedTime) {

    Date date = TimeZoneConverter.convertTime(timestamp);

    assertEquals(expectedConvertedTime, date);
}


private static Stream<Arguments> timestampsWithOffsetWithCorrectlyConvertedTime() throws ParseException {

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return Stream.of(
            Arguments.of("2022-08-13 10:13:56.48382+00", sdf.parse("2022-08-13 12:13:56")),
            Arguments.of("2022-09-20 22:33:16.4271382-03", sdf.parse("2022-09-21 03:33:16")),
            Arguments.of("2022-11-25 16:00:00.482+12", sdf.parse("2022-11-25 05:00:00")),
            Arguments.of("2022-03-03 11:33:16.483829+02", sdf.parse("2022-03-03 10:33:16"))

    );
}

问题原因及解决方案

核心问题:测试代码依赖默认时区

你的转换逻辑本身没有问题——用ZonedDateTime显式处理时区,最终返回的Date是无时区的瞬时时间戳。但测试代码里的SimpleDateFormat会依赖JVM默认时区:

  • 本地环境你已经把默认时区设为Europe/Berlin,所以sdf.parse()解析出来的Date和转换逻辑返回的瞬时值一致;
  • 流水线服务器的默认时区不是Europe/Berlin,导致解析出的Date对应时间戳和预期不符,断言失败。

解决步骤

  1. 修改测试代码,让SimpleDateFormat显式指定时区
    不要依赖默认时区,给SimpleDateFormat设置Europe/Berlin时区:

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("Europe/Berlin")); // 新增该行
    

    这样不管JVM默认时区是什么,解析出来的Date都是对应Berlin时区的瞬时值,和转换逻辑输出匹配。

  2. 无需修改流水线时区(可选)
    只要测试代码不依赖默认时区,流水线的时区设置就不会影响测试结果。如果有其他场景需要,也可以在流水线脚本中设置时区环境变量:

    export TZ="Europe/Berlin"
    

    但优先保证代码本身的时区独立性更可靠。

  3. 建议统一使用Java 8+日期API
    转换逻辑已经用了ZonedDateTime这类新API,测试代码仍用线程不安全的SimpleDateFormat,建议替换为DateTimeFormatter:

    private static Stream<Arguments> timestampsWithOffsetWithCorrectlyConvertedTime() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                .withZone(ZoneId.of("Europe/Berlin"));
        return Stream.of(
                Arguments.of("2022-08-13 10:13:56.48382+00", Date.from(formatter.parse("2022-08-13 12:13:56", Instant::from))),
                Arguments.of("2022-09-20 22:33:16.4271382-03", Date.from(formatter.parse("2022-09-21 03:33:16", Instant::from))),
                Arguments.of("2022-11-25 16:00:00.482+12", Date.from(formatter.parse("2022-11-25 05:00:00", Instant::from))),
                Arguments.of("2022-03-03 11:33:16.483829+02", Date.from(formatter.parse("2022-03-03 10:33:16", Instant::from)))
        );
    }
    

    这样更安全,也彻底避免时区依赖问题。


内容的提问来源于stack exchange,提问作者bob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 12:30:59