Java日期库中一年中某周的不一致

时间:2018-10-23 11:41:18

标签: java hive simpledateformat datetime-format week-number

根据https://en.wikipedia.org/wiki/ISO_8601#Week_dates,工作日从星期一开始。但是,从Java中,如果您尝试以两种不同的方式提取星期数,那么如果日期是星期日,则会出现两种不同的输出。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class TestDate {
    public static void main(String[] args) throws ParseException {
        final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        final SimpleDateFormat weekFormatter = new SimpleDateFormat("ww");
        String date = "2018-10-21";
        System.out.println(weekFormatter.format(formatter.parse(date)));
        Calendar calendar = Calendar.getInstance();
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setTime(formatter.parse(date));
        System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));
    }
}

输出:

43
42

这是前后矛盾吗?

这只是我编写的用于重现该问题的测试程序,我注意到Hive中的问题,如下所示:

0: jdbc:hive2://zk0-something> select from_unixtime(t, 'ww'), weekofyear(from_unixtime(t, 'yyyy-MM-dd')) from (select 1540122033 as t) a;
+------+------+--+
| _c0  | _c1  |
+------+------+--+
| 43   | 42   |
+------+------+--+
1 row selected (0.388 seconds)
0: jdbc:hive2://zk0-something>

1 个答案:

答案 0 :(得分:4)

java.time

    String date = "2018-10-21";
    LocalDate ld = LocalDate.parse(date);
    int weekOfYear = ld.get(WeekFields.ISO.weekOfYear());
    System.out.println(weekOfYear);

输出:

  

42

由于您对ISO 8601规则for week numbers感兴趣,因此请使用WeekFields.ISOLocalDate获取与星期相关的数据。如果您愿意,也可以使用formatter

    DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("ww", Locale.FRANCE);
    System.out.println(ld.format(weekFormatter));

输出相同:

  

42

传递给DateTimeFormatter.ofPattern的语言环境确定星期方案。如果我通过Locale.US,则得到43。

我建议您使用现代Java日期和时间API java.time,并远离诸如SimpleDateFormatCalendar之类的旧日期时间类。旧的设计欠佳,现代的则更好用。

您的代码出了什么问题?

过时的SimpleDateFormat类和现代的DateTimeFormatter都从其区域设置采用周编号方案。如果未为格式化程序指定语言环境,则它将使用JVM的默认语言环境。因此,例如,如果JVM具有美国语言环境,那么格式化程序将在您的第一个示例中显示43,因为在美国,今年10月21日,星期日是第43周。如果语言环境是法语,则它将打印42,因为那一天是星期几。 42在法国。法国遵循ISO 8601标准,美国未遵循。

在您的示例中,将Calendar的一周的第一天设置为星期一会导致星期数像您期望的那样为42。但是,情况并非总是如此。周数不仅可以通过一周的第一天来定义,还可以通过周1的定义来定义。通过链接:

  

一年中的第一个ISO周最多可以有三天   实际上是在公历年结束如果它们是   星期一,星期二和星期三。同样,一年中的最后一个ISO周   可能最多有公历三天   开始的一年;如果是星期五,星期六和星期日。的   每个ISO周的星期四始终是公历年   用ISO周编号年份表示。

哪个星期是第1周的美国人定义是不同的:在美国,1月1日始终是第1周。因此,如果您的Calendar是使用美国语言环境创建的,则将第一个星期的第一天设置为星期一足以遵循ISO 8601规则。巧合的是,尽管如此,2018年的周数还是一样。