Java和Python的年度周数计算差异

3 投票
5 回答
2497 浏览
提问于 2025-04-16 16:02

我有两个部分的实现,一个是用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 个回答

1

总结

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.DateCalendarSimpleDateFormat

Joda-Time 项目现在处于 维护模式,建议迁移到 java.time 类。

想了解更多,可以查看 Oracle 教程。在 Stack Overflow 上也可以找到很多示例和解释。相关规范是 JSR 310

你可以直接将 java.time 对象与数据库交换。使用符合 JDBC 驱动JDBC 4.2 或更高版本。无需使用字符串,也无需使用 java.sql.* 类。

在哪里可以获取 java.time 类?

ThreeTen-Extra 项目为 java.time 扩展了额外的类。这个项目是未来可能添加到 java.time 的试验场。你可以在这里找到一些有用的类,比如 IntervalYearWeekYearQuarter,以及 更多

2

在Java中,奇怪的月份编号问题并不是全部的解决方案。Java使用本地化的规则来计算周数(不同国家对哪一天是第一天、哪一周是第一周的规定不同)。而Python的isocalendar函数则按照ISO 8601的周编号规则来计算,这个规则规定一周从星期一开始,并且每年的第一周总是包含1月4日。

4

在Java中,月份是从0开始计算的,所以你需要这样做:

cal.set(y, m - 1, d); 

补充说明:

正如jarnbjo提到的,Calendar需要设置成可以生成ISO 8601的周数,具体可以参考这个链接

cal.setMinimalDaysInFirstWeek(4);    
cal.setFirstDayOfWeek(Calendar.MONDAY);

撰写回答