为什么2010年12月31日作为一年中的一周返回1?

例如:

Calendar c = Calendar.getInstance(); DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); c.setTime( sdf.parse("31/12/2010")); out.println( c.get( Calendar.WEEK_OF_YEAR ) ); 

打印1

Joda时间也是如此。

🙂

每周的定义取决于Locale

在其他post中讨论了如何在美国定义它。 例如在德国( DIN 1355-1 / ISO 8601 ):第一周*是第一周,在新的一年中有4天或更多天。

*周的第一天是星期一,星期的最后一天是星期日

Java的Calendar关注语言环境。 例如:

 public static void main(String[] args) throws ParseException { DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); Date lastDec2010 = sdf.parse("31/12/2010"); Calendar calUs = Calendar.getInstance(Locale.US); calUs.setTime(lastDec2010); Calendar calDe = Calendar.getInstance(Locale.GERMAN); calDe.setTime(lastDec2010); System.out.println( "us: " + calUs.get( Calendar.WEEK_OF_YEAR ) ); System.out.println( "de: " + calDe.get( Calendar.WEEK_OF_YEAR ) ); } 

打印:

 us: 1 de: 52 

添加对于美国(我可以想到墨西哥也是如此)1。一周是1月1日所属的那一周。 – 因此,如果1. Januar是星期六,那么星期五(星期三)是同一周,在这种情况下,这一天属于2011年的一周。

为WEEK_OF_YEAR字段计算的值范围为1到53.一年的第1周是从getFirstDayOfWeek()开始的最早的七天时段,该时间段至少包含该年的getMinimalDaysInFirstWeek()天。 因此,它取决于getMinimalDaysInFirstWeek(),getFirstDayOfWeek()和1月1日的星期几的值。一年的第1周和第二年的第1周之间的周数从2到52或53顺序编号(如需要)。

要确定该周是2010年的最后一周还是2011年的第一周,Java使用的是getMinimalDaysInFirstWeek javadoc 。 如果该方法返回7,则该周的所有日期都是同一年的第一周是第一周,如果它返回1,那么第一周与下一年的任何日期是下一年的第一周。

在这种情况下,2011年1月的第一天是在星期六所以它被认为是2011年的第一周,只要你想要一周一周被认为已经是明年的第一周,如果你不这样做然后做:

 Calendar c = Calendar.getInstance(); c.setMinimalDaysInFirstWeek(7);//anything more than 1 will work in this year DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); c.setTime( sdf.parse("31/12/2010")); System.out.println( c.get( Calendar.WEEK_OF_YEAR ) ); 

收益:

 52 

IIRC,1月1日的第一周是第1周。
这就是为什么第1周将于12/31/2010返回。
试试12/31/2011,你会得到52。

编辑:周是特定于区域设置,有时定义为星期日 – 星期六,有时定义为星期一 – 星期日

这是因为本周的开始是依赖于本地的。

在美国,第1周从1月1日之前的星期日开始。2010年是12月26日。这就是12月31日仍然是第1周的原因。

在欧洲,第1周从1月1日之前的星期一开始。2010年是12月27日。这就是为什么欧洲12月31日仍然是第1周。

TL;博士

 java.time.LocalDate.parse( "31/12/2010" , DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( Locale.UK ) ) .get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) 

52

或者,添加一个库,然后……

 org.threeten.extra.YearWeek.from( // Convert from a `LocalDate` object to a `YearWeek` object representing the entire week of that date's week-based year. java.time.LocalDate.parse( "31/12/2010" , DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( Locale.UK ) ).getWeek() // Extract an integer number of that week of week-based-year, either 1-52 or 1-53 depending on the year. 

52

java.time

正如其他人所指出的,一周的定义因旧的java.util.Calendar类中的Locale而异。

这个麻烦的类及其合作伙伴java.util.Date已被Java 8及更高版本中内置的java.time框架所取代。

IsoFields类使用ISO 8601标准定义一周:Week始终在星期一开始,而#1周持有日历年的第一个星期四。

获取当前时刻。

 ZoneId zoneId = ZoneId.of ( "America/Montreal" ); ZonedDateTime now = ZonedDateTime.now ( zoneId ); 

询问标准的基于周的年份。

 int week = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR ); int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR ); 

标准周定义

有很多方法可以定义“一周”和“一年中的第一周”。

但是,有一个主要的标准定义 : ISO 8601标准 。 该标准定义了一年中的几周,包括一年中的第一周。

这一周是第一个星期四

标准周从星期一开始,到星期日结束。

基于周的年份的第1周具有该日历年的第一个星期四。

java.time类通过IsoFields类支持ISO 8601周, IsoFields三个实现TemporalField常量:

  • WEEK_OF_WEEK_BASED_YEAR
  • WEEK_BASED_YEAR
  • WEEK_BASED_YEARS

调用LocalDate::get访问TemporalField

 LocalDate ld = LocalDate.parse( "2010-12-31" ) ; int weekOfWeekBasedYear = ld.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ; int yearOfWeekBasedYear = ld.get( IsoFields.WEEK_BASED_YEAR ) ; 

ld.toString():2010-12-31

weekOfWeekBasedYear:52

yearOfWeekBasedYear:2010

ISO 8601字符串格式

ISO 8601标准定义了文本格式以及基于周的年度值的含义: yyyy-Www 。 对于特定日期,请在周一至周日添加星期几为1-7的星期几: yyyy-Www-d

构造这样一个字符串。

 String outputWeek = String.format( "%04d" , yearOfWeekBasedYear ) + "-W" + String.format( "%02d" , weekOfWeekBasedYear ) ; String outputDate = outputWeek + "-" + ld.getDayOfWeek().getValue() ; 

2010-W52-5

YearWeek

如果将ThreeTen-Extra库添加到项目中,这项工作会更容易。 然后使用YearWeek类。

 YearWeek yw = YearWeek.from( ld ) ; // Determine ISO 8601 week of a `LocalDate`. 

生成标准字符串。

 String output = yw.toString() ; 

2010-W52

并解析。

 YearWeek yearWeek = YearWeek.parse( "2010-W52" ) ; 

yearWeek.toString():2010-W52

确定日期。 为周日 – 周日的星期几传递java.time.DayOfWeek枚举对象。

 LocalDate localDate = yw.atDay( DayOfWeek.MONDAY ) ; 

localDate.toString():2010-12-27

我强烈建议将此库添加到您的项目中。 然后你可以传递智能对象而不是愚蠢的整数。 这样做可以使您的代码更加自我记录,提供类型安全性并确保有效值。


关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

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

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

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

从哪里获取java.time类?

  • Java SE 8Java SE 9及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android, ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。


有关更多详细信息,请参阅我对类似问题的回答 :

  • java得到一个约会的一年中的一周

…并在类似的问题上看到我的答案 :

  • 如何从Java中的ISO8601周数计算日期

我觉得这是一种更好的方法。

 Calendar c = Calendar.getInstance(); DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); c.setTime( sdf.parse("31/12/2010")); int thisWeek = c.get(Calendar.WEEK_OF_YEAR); if (c.get(Calendar.MONTH) == Calendar.DECEMBER && thisWeek == 1) thisWeek = 53; System.out.println(thisWeek); 

这里的主要假设是,对于12月份,本周不能为1,因此只返回53。

如果我在某处错了,请提出建议,以及为什么在Java日历中没有实现。