DateFormat UnitTest 在 Jenkins 中失败但在本地失败

DateFormat UnitTest fails in Jenkins but not locally

我对一个服务进行了单元测试,当我执行 Jenkins 作业时,每个测试方法都正确通过,只有一个除外。
但是这个测试方法在我的机器上工作,使用 Eclipse 和使用 mvn 命令。

    // TARGET_RUN_DATE_OF_YEAR = "2018-01-01"
    @Test
    public void dateToTimestamp() {
        Service service = getService();

        String df = "YYYY-MM-dd";
        String invalid = "INVALID";

        // Check success
        Timestamp timestamp = service.dateToTimestamp(TARGET_RUN_DATE_OF_YEAR, df);
        Assert.assertEquals(service.getTodayTimestamp(), timestamp); // <-- Fail here
        // Check failure
        Assert.assertNull(service.dateToTimestamp(TARGET_RUN_DATE_OF_YEAR, invalid));
        Assert.assertNull(service.dateToTimestamp(invalid, df));
    }

服务有多种方法:

服务中的dateToTimestamp方法:

    private Timestamp dateToTimestamp(String date, DateFormat df) throws ParseException {
        return new Timestamp(df.parse(date).getTime());
    }

    @Override
    public Timestamp dateToTimestamp(String date, String dateFormatString) {
        try {
            DateFormat df = new SimpleDateFormat(dateFormatString);
            return dateToTimestamp(date, df);
        } catch (Exception e) {
            log.warn("Exception during conversion of date to timestamp, exception : {}", e);
            return null;
        }
    }

正如我之前所说,测试在我的电脑上完美运行,但在 Jenkins 上却不行(向此方法添加 @Ignore 注释,使工作成功)。
启动作业时,出现此错误:

Failed tests: dateToTimestamp(com.test.service.ServiceImplTest): expected:<2018-01-01 00:00:00.0> but was:<2017-12-31 00:00:00.0>

我可以保证的是,即使在 Jenkins 中,dateToTimestamp 方法也采用参数 TARGET_RUN_DATE_OF_YEAR,即 "2018-01-01" 和日期格式字符串 "YYYY-MM-dd"。但仍然 returns 2017-12-31 00:00:00.0 作为时间戳。

有什么想法吗?

java.time

我假设您想要一个 Timestamp 用于您的 SQL 数据库。不要在 2019 年使用 Timestamp。class 设计不佳且早已过时。

  • 如果 SQL 端的数据类型是 timestamp with time zone(时间戳应该是),请在 Java.
  • 中使用 OffsetDateTime
  • 如果在 SQL 方面您只需要 timestamp(不带时区),请在 Java 中使用 LocalDateTime

代码示例:

    String dateString = "2018-01-01";
    OffsetDateTime odt = LocalDate.parse(dateString)
            .atStartOfDay()
            .atOffset(ZoneOffset.UTC);

    System.out.println(odt);

输出为:

2018-01-01T00:00Z

现在您可以将 OffsetDateTime 传递给 JDBC,方法如下:

    yourPreparedStatement.setObject(4, odt);

你的代码出了什么问题?

我相信您同时遇到了两个问题:

  • 在您的格式模式字符串中使用大写 YYYY 是不正确的。大写 Y 用于基于周的年份,仅对周数有用。使用旧的和麻烦的 SimpleDateFormat 你需要小写的 y 作为年份。
  • (The JVMs on) 你的机器和 Jenkins 服务器有不同的默认语言环境。

演示:

    String dateString = "2018-01-01";
    String dateFormatString = "YYYY-MM-dd"; // Incorrect format pattern string
    DateFormat df = new SimpleDateFormat(dateFormatString);
    System.out.println(df.parseObject(dateString));

我电脑上的输出(丹麦语言环境,Europe/Copenhagen 时区):

Mon Jan 01 00:00:00 CET 2018

但是,如果我首先这样做:

    Locale.setDefault(Locale.US);

——那么上面代码片段的输出是不同的,即:

Sun Dec 31 00:00:00 CET 2017

发生的事情是 SimpleDateFormat 放弃了根据基于周的年、月和日确定确切日期,而是只为您提供基于年的周的第一个日期。这是 SimpleDateFormat 的典型行为,给你一个不可能正确的结果,但仍然假装一切都很好。在某些地区,一周从星期一开始,您会得到 Mon Jan 01,这 巧合 与您的字符串一致。在其他地区,例如美国,一周从星期日开始,因此您得到的是前一年的 12 月 31 日星期日。

Link