java 一年中第几周的日期库不一致
Inconsistency in java date library for week of year
根据 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>
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 rules for week numbers, use WeekFields.ISO
for getting week related data from a LocalDate
. You may also use a formatter感兴趣,如果你喜欢:
DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("ww", Locale.FRANCE);
System.out.println(ld.format(weekFormatter));
输出相同:
42
传递给 DateTimeFormatter.ofPattern
的区域设置决定了星期方案。如果我通过 Locale.US
,我得到 43.
我建议您使用 java.time,现代的 Java 日期和时间 API,并远离旧的日期时间 class,例如 [=16] =] 和 Calendar
。旧的设计不佳,现代的更好用。
你的代码出了什么问题?
过时的 SimpleDateFormat
class 和现代的 DateTimeFormatter
都从他们的语言环境中获取他们的周编号方案。如果没有为格式化程序指定语言环境,它将使用 JVM 的默认语言环境。因此,如果 JVM 具有美国语言环境,例如,格式化程序将在您的第一个示例中打印 43,因为在美国,今年 10 月 21 日星期日是第 43 周。如果语言环境是法语,它将打印 42,因为那一天是第 43 周42 在法国。法国遵循 ISO 8601 标准,美国没有。
在您的示例中,将 Calendar
的一周的第一天设置为星期一会导致周数如您预期的那样为 42。然而,情况并非总是如此。周数不仅由一周的第一天定义,而且由第 1 周的定义定义。来自您的 link:
The first ISO week of a year may have up to three days that are
actually in the Gregorian calendar year that is ending; if they are
Monday, Tuesday and Wednesday. Similarly, the last ISO week of a year
may have up to three days that are actually in the Gregorian calendar
year that is starting; if they are Friday, Saturday, and Sunday. The
Thursday of each ISO week is always in the Gregorian calendar year
denoted by the ISO week-numbering year.
美国对第 1 周的定义不同:在美国,1 月 1 日始终在第 1 周。因此,如果您的 Calendar
是使用美国语言环境创建的,请将其一周的第一天设置为星期一仅仅遵循 ISO 8601 规则是不够的。巧合的是,2018 年的周数是一致的。
根据 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>
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 rules for week numbers, use WeekFields.ISO
for getting week related data from a LocalDate
. You may also use a formatter感兴趣,如果你喜欢:
DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("ww", Locale.FRANCE);
System.out.println(ld.format(weekFormatter));
输出相同:
42
传递给 DateTimeFormatter.ofPattern
的区域设置决定了星期方案。如果我通过 Locale.US
,我得到 43.
我建议您使用 java.time,现代的 Java 日期和时间 API,并远离旧的日期时间 class,例如 [=16] =] 和 Calendar
。旧的设计不佳,现代的更好用。
你的代码出了什么问题?
过时的 SimpleDateFormat
class 和现代的 DateTimeFormatter
都从他们的语言环境中获取他们的周编号方案。如果没有为格式化程序指定语言环境,它将使用 JVM 的默认语言环境。因此,如果 JVM 具有美国语言环境,例如,格式化程序将在您的第一个示例中打印 43,因为在美国,今年 10 月 21 日星期日是第 43 周。如果语言环境是法语,它将打印 42,因为那一天是第 43 周42 在法国。法国遵循 ISO 8601 标准,美国没有。
在您的示例中,将 Calendar
的一周的第一天设置为星期一会导致周数如您预期的那样为 42。然而,情况并非总是如此。周数不仅由一周的第一天定义,而且由第 1 周的定义定义。来自您的 link:
The first ISO week of a year may have up to three days that are actually in the Gregorian calendar year that is ending; if they are Monday, Tuesday and Wednesday. Similarly, the last ISO week of a year may have up to three days that are actually in the Gregorian calendar year that is starting; if they are Friday, Saturday, and Sunday. The Thursday of each ISO week is always in the Gregorian calendar year denoted by the ISO week-numbering year.
美国对第 1 周的定义不同:在美国,1 月 1 日始终在第 1 周。因此,如果您的 Calendar
是使用美国语言环境创建的,请将其一周的第一天设置为星期一仅仅遵循 ISO 8601 规则是不够的。巧合的是,2018 年的周数是一致的。