Java和Python的年度周数计算差异
我有两个部分的实现,一个是用Python写的,另一个是用Java写的。现在的问题是,我发现它们计算的年份周数不一样。例如:
Java代码:
private static int getWeekOfYear(int y, int m, int d) {
Calendar cal = Calendar.getInstance();
cal.setMinimalDaysInFirstWeek(4);
cal.set(y, m, d);
return cal.get(Calendar.WEEK_OF_YEAR);
}
System.out.println(getWeekOfYear(2010, 7, 1));
Java的结果是:31
Python代码:
print datetime(2010, 7, 1, 0, 0).isocalendar()[1]
Python的结果是:26
现在我该怎么做才能让它们的结果一致呢?顺便说一下,我希望Java的计算方式完全按照Python来。
5 个回答
总结
LocalDate.of( 2010 , Month.JULY , 1 )
.get (
IsoFields.WEEK_OF_WEEK_BASED_YEAR
)
26
详细信息
正如其他人提到的,你的月份参数不对,因为在那个 Calendar
类中,月份是从零开始计算的,这样的计数方式有点疯狂。
还有人提到,Calendar
类对“周”的定义是根据 Locale
(地区)来变化的,而不是像你在 Python 代码中使用的 ISO 8601 标准周定义。
java.time
另一个问题是:你在使用非常麻烦的旧日期时间类,这些类是早期 Java 版本的一部分。现在这些类已经过时,被 java.time 类替代了。
如果你只需要日期,不需要具体的时间和时区,可以使用 LocalDate
。为了让代码更易读,可以使用方便的 Month
枚举。
LocalDate ld = LocalDate.of( 2010 , Month.JULY , 1 ) ;
或者使用月份数字,正常的编号是 1-12,分别代表一月到十二月。
LocalDate ld = LocalDate.of( 2010 , 7 , 1 ) ;
计算标准的 ISO 8601 周编号。使用 LocalDate::get
方法来获取相关的值。传入枚举元素 IsoFields.WEEK_OF_WEEK_BASED_YEAR
来指明你想要的值。
int w = ld.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
YearWeek
小提示:如果你经常处理这一年的周数,可以看看 YearWeek
类,它在 ThreeTen-Extra 项目中。
YearWeek yw = YearWeek.from( ld ) ;
关于 java.time
java.time 框架是内置于 Java 8 及以后的版本中的。这些类取代了麻烦的旧 遗留 日期时间类,比如 java.util.Date
、Calendar
和 SimpleDateFormat
。
Joda-Time 项目现在处于 维护模式,建议迁移到 java.time 类。
想了解更多,可以查看 Oracle 教程。在 Stack Overflow 上也可以找到很多示例和解释。相关规范是 JSR 310。
你可以直接将 java.time 对象与数据库交换。使用符合 JDBC 驱动 的 JDBC 4.2 或更高版本。无需使用字符串,也无需使用 java.sql.*
类。
在哪里可以获取 java.time 类?
- Java SE 8、Java SE 9、Java SE 10 及更高版本
- 内置的。
- 作为标准 Java API 的一部分,带有实现。
- Java 9 增加了一些小功能和修复。
- Java SE 6 和 Java SE 7
- 大部分 java.time 的功能已经被移植到 Java 6 和 7 中,使用 ThreeTen-Backport。
- Android
- 较新版本的 Android 包含了 java.time 类的实现。
- 对于早期的 Android(<26),可以使用 ThreeTenABP 项目来适配 ThreeTen-Backport(上面提到的)。可以查看 如何使用 ThreeTenABP…。
ThreeTen-Extra 项目为 java.time 扩展了额外的类。这个项目是未来可能添加到 java.time 的试验场。你可以在这里找到一些有用的类,比如 Interval
、YearWeek
、YearQuarter
,以及 更多。
在Java中,奇怪的月份编号问题并不是全部的解决方案。Java使用本地化的规则来计算周数(不同国家对哪一天是第一天、哪一周是第一周的规定不同)。而Python的isocalendar函数则按照ISO 8601的周编号规则来计算,这个规则规定一周从星期一开始,并且每年的第一周总是包含1月4日。
在Java中,月份是从0开始计算的,所以你需要这样做:
cal.set(y, m - 1, d);
补充说明:
正如jarnbjo提到的,Calendar
需要设置成可以生成ISO 8601的周数,具体可以参考这个链接:
cal.setMinimalDaysInFirstWeek(4);
cal.setFirstDayOfWeek(Calendar.MONDAY);