了解java.util.Calendar WEEK_OF_YEAR

kha*_*hik 10 java datetime calendar

我试图理解java.util.Calendar.get(java.util.Calendar.WEEK_OF_YEAR)它是如何工作的,但似乎我错过了一些观点.

String time = "1998-12-31"; // year month day
java.util.Calendar date = java.util.Calendar.getInstance();
date.setTime((new java.text.SimpleDateFormat("yyyy-MM-dd")).parse(time));
System.err.println("Week of year = " + date.get(java.util.Calendar.WEEK_OF_YEAR));
// Week of year = 1 Why ???
Run Code Online (Sandbox Code Playgroud)

为什么date.get(java.util.Calendar.WEEK_OF_YEAR)在一年的最后一周返回1?

而且,WEEK_OF_YEAR因为"1998-01-01"是1而且"1998-12-23"它是52.
有没有人对这种行为有解释?

npe*_*npe 11

来自java.util.Calendar javadoc:

第一周

日历使用两个参数定义特定于语言环境的七天工作周:一周的第一天和第一周的最小天数(从1到7).构建Calendar时,这些数字取自语言环境资源数据.它们也可以通过设置其值的方法明确指定.

设置或获取WEEK_OF_MONTH或WEEK_OF_YEAR字段时,日历必须将月份或年份的第一周确定为参考点.一个月或一年的第一周定义为从getFirstDayOfWeek()开始并且至少包含该月或年的getMinimalDaysInFirstWeek()天的最早七天.周数在第一周之前编号为......,-1,0; 周数为2,3,......跟随它.请注意,get()返回的规范化编号可能不同.例如,特定的日历子类可以指定一年的第1周之前的一周作为前一年的第n周.

所以它是特定于语言环境的.在您的情况下,如果一周包含新年的天数,则将其计为新年的第1周.

您可以使用Calendar#setMinimalDaysInFirstWeek(int)更改此行为.

  • 那么,本周不可能两者兼而有之 - 旧年的最后一年,也是新年的第一周.这是一个或另一个. (3认同)

Bas*_*que 6

TL;博士

java.time.LocalDate.parse( "1998-12-31" )
    .get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )
Run Code Online (Sandbox Code Playgroud)

53

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

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.
    LocalDate.parse( "1998-12-31" )   // Parse string into a `LocalDate` objects. 
).getWeek()                           // Extract an integer number of that week of week-based-year, either 1-52 or 1-53 depending on the year.
Run Code Online (Sandbox Code Playgroud)

53

细节

我试图了解java.util.Calendar.get(java.util.Calendar.WEEK_OF_YEAR)的工作原理

别!那堂课是个烂摊子,最好忘了.

npe答案是正确的.在Calendar,一周的定义因地区而异.一个善意的功能,但令人困惑.

标准周定义

有很多方法可以定义"一周"和"一年中的第一周".

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

这一周是第一个星期四

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

标准的基于周的年份的第1周具有日历年的第一个星期四.

java.time

java.time类取代了旧的麻烦日期时间类.这些现代类通过类支持ISO 8601周IsoFields,包含三个实现的常量TemporalField:

打电话LocalDate::get访问TemporalField.

LocalDate ld = LocalDate.parse( "1998-12-31" ) ;
int weekOfWeekBasedYear = ld.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int yearOfWeekBasedYear = ld.get( IsoFields.WEEK_BASED_YEAR ) ;
Run Code Online (Sandbox Code Playgroud)

ld.toString():1998-12-31

weekOfWeekBasedYear:53

yearOfWeekBasedYear:1998

请注意,1999年新日历年的第一天,同一周,也就是1998年的第53周.

LocalDate firstOf1999 = ld.plusDays( 1 );
int weekOfWeekBasedYear_FirstOf1999 = firstOf1999.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int yearOfWeekBasedYear_FirstOf1999 = firstOf1999.get( IsoFields.WEEK_BASED_YEAR ) ;
Run Code Online (Sandbox Code Playgroud)

firstOf1999.toString():1999-01-01

weekOfWeekBasedYear_FirstOf1999:53

yearOfWeekBasedYear_FirstOf1999:1998

ISO 8601字符串格式

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

构造这样一个字符串.

String outputWeek = ld.format( DateTimeFormatter.ISO_WEEK_DATE ) ;  // yyyy-Www 
Run Code Online (Sandbox Code Playgroud)

1998年-W53

String outputDate = outputWeek + "-" + ld.getDayOfWeek().getValue() ;  // yyyy-Www-d
Run Code Online (Sandbox Code Playgroud)

1998年,W53-4

YearWeek

如果将ThreeTen-Extra库添加到项目中,这项工作会容易得多.然后使用该YearWeek课程.

YearWeek yw = YearWeek.from( ld ) ;  // Determine ISO 8601 week of a `LocalDate`. 
Run Code Online (Sandbox Code Playgroud)

生成标准字符串.

String output = yw.toString() ;
Run Code Online (Sandbox Code Playgroud)

1998年-W53

并解析.

YearWeek yearWeek = YearWeek.parse( "1998-W53" ) ;  
Run Code Online (Sandbox Code Playgroud)

yearWeek.toString():1998-W53

确定日期.在java.time.DayOfWeek星期一到星期日传递enum对象.

LocalDate localDate = yw.atDay( DayOfWeek.MONDAY ) ;
Run Code Online (Sandbox Code Playgroud)

localDate.toString():1998-12-28

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


关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.

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

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

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

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.


乔达时间

UPDATE:乔达时间的项目现在处于维护模式,与团队的建议迁移java.time类.此部分保留为历史记录.

优秀的Joda-Time框架使用ISO 8601作为默认值.它的课程包括这周的信息.Joda-Time是与Java捆绑在一起的臭名昭着的java.util.Date和java.util.Calendar类的流行替代品.

示例代码

下面是一些示例代码,用于获取当前日期时间第一周第一天的第一个时刻.

请注意withTimeAtStartOfDay获取当天第一时刻的电话.

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );

DateTime now = new DateTime( timeZone );
DateTime firstWeekStart = now.withWeekOfWeekyear(1).withDayOfWeek(1).withTimeAtStartOfDay();
DateTime firstWeekStop = firstWeekStart.plusWeeks( 1 );
Interval firstWeek = new Interval( firstWeekStart, firstWeekStop );
Run Code Online (Sandbox Code Playgroud)

转储到控制台......

System.out.println( "now: " + now );
System.out.println( "firstWeekStart: " + firstWeekStart );
System.out.println( "firstWeekStop: " + firstWeekStop );
System.out.println( "firstWeek: " + firstWeek );
Run Code Online (Sandbox Code Playgroud)

跑的时候......

now: 2014-02-07T12:49:33.623+01:00
firstWeekStart: 2013-12-30T00:00:00.000+01:00
firstWeekStop: 2014-01-06T00:00:00.000+01:00
firstWeek: 2013-12-30T00:00:00.000+01:00/2014-01-06T00:00:00.000+01:00
Run Code Online (Sandbox Code Playgroud)