Java 8 DateTimeFormatter 踩坑

1. 问题描述

本月是 2019 年的第一个月,除了迎接新年之外同样也迎来了跨年带来的关于时间参数的 bug。

废话不多说,进入正题。之前写了一个任务调度系统,支持以周为粒度进行任务的调度。针对任务的每次运行都会产生一个批次号,一般以 任务名_<年份><周数> 的格式编号(比如 job_201850 ),但是在 2018 年 12 月 31 日却发现了自然周的序号发生了跳跃。

2. 分析出问题的代码

查看了一下代码,问题出在 DateTime 格式化上,原来的 DateTimeFormatter 的定义如下:

DateTimeFormatter.ofPattern("yyyyww")

预想的是 yyyy 代表年,ww 代表自然周,对于 2018 年 12 月 31 日原本预计得到的结果是 201853,但是此处 ww 得到的结果竟然是 01。

百思不得其解之下上网查了一下,发现原来错的不是 ww,而是 yyyy

一般来说 yyyy 是和自然年的月份、日期搭配的,对于和自然周的搭配是要用 YYYY 的。代码如下:

@Test
public void testForWeek() {
    DateTimeFormatter rightFormatter = DateTimeFormatter.ofPattern("YYYYww");
    DateTimeFormatter wrongFormatter = DateTimeFormatter.ofPattern("yyyyww");

    LocalDateTime now = LocalDateTime.of(2019, 1, 7, 0, 30, 0);
    System.out.println(now);
    String right = rightFormatter.format(now);
    String wrong = wrongFormatter.format(now);
    System.out.println("right = " + right);
    System.out.println("wrong = " + wrong);

    now = LocalDateTime.of(2018, 12, 31, 0, 30, 0);
    System.out.println(now);
    right = rightFormatter.format(now);
    wrong = wrongFormatter.format(now);
    System.out.println("right = " + right);
    System.out.println("wrong = " + wrong);
}

输出结果:

2019-01-07T00:30
right = 201902
wrong = 201902
2018-12-31T00:30
right = 201901
wrong = 201801

也就是说 2018 年 12 月 31 日实际上算是 2019 年的第一周,这也算刷新了我的认知!

3. 后续订正

今天又出幺蛾子了!

今天是 2019-04-28 星期日,按理来说今天应该是 2019 年的第十七周的最后一天。可是今天用 YYYYww 格式化日期得到的却是 201918,代码如下:

  @Test
  public void testForDateTimeFormatter() {

    LocalDateTime now = LocalDateTime.now();
    DateTimeFormatter week = DateTimeFormatter.ofPattern("YYYYww");

    LocalDateTime yesterday = now.plusDays(-1);
    System.out.println("yesterday = " + yesterday);
    System.out.println(yesterday.getDayOfWeek().getValue());
    System.out.println(week.format(yesterday));
    System.out.println(this.getWeekFormat(yesterday));

    System.out.println("now = " + now);
    System.out.println(now.getDayOfWeek().getValue());
    System.out.println(week.format(now));
    System.out.println(this.getWeekFormat(now));

    LocalDateTime tomorrow = now.plusDays(1);
    System.out.println("tomorrow = " + tomorrow);
    System.out.println(tomorrow.getDayOfWeek().getValue());
    System.out.println(week.format(tomorrow));
    System.out.println(this.getWeekFormat(tomorrow));
  }

  private String getWeekFormat(LocalDateTime localDateTime) {
    int year = localDateTime.get(IsoFields.WEEK_BASED_YEAR);
    int weekNum = localDateTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
    return year + String.format("%02d", weekNum);
  }

输出的内容为:

yesterday = 2019-04-27T10:00
6
201917
201917
now = 2019-04-28T10:00
7
201918
201917
tomorrow = 2019-04-29T10:00
1
201918
201918

也就是说按照 YYYYww 格式化的时候在星期日就会进行周号的递增!

正确格式化的的方式应该是:

  private String getWeekFormat(LocalDateTime localDateTime) {
    int year = localDateTime.get(IsoFields.WEEK_BASED_YEAR);
    int weekNum = localDateTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
    return year + String.format("%02d", weekNum);
  }

发表评论

电子邮件地址不会被公开。 必填项已用*标注