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

31
例如:
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时间也会发生相同的情况。

:)


9
也许因为它确实是在2011年的第一周? - rsenna
@Ralph,es_MX,墨西哥,我期望的不是1。 - OscarRyz
我已经扩展了我的答案,解释了为什么12月31日属于美国的第1周。- 但是我不知道墨西哥是怎样的。 - Ralph
类似问题:java获取给定日期的一年中的周数 - Basil Bourque
6个回答

64

周年定义与所使用的语言环境(Locale)有关。

在美国,如何定义已经在其他帖子中讨论过。例如在德国 (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月1日是星期六,则前一天(12月31日)属于同一周,在这种情况下,这一天属于2011年的第1周。


11
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月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

5

简而言之

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 标准来定义一周:一周始终从星期一开始,第一周包含日历年的第一个星期四。
获取当前时刻。
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周,该类包含三个实现了TemporalField的常量:

调用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

我强烈建议将这个库添加到你的项目中。这样,你可以传递智能对象而不是愚蠢的整数。这样做可以使你的代码更加自我说明,提供type-safety,并确保有效值。


关于 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类?

ThreeTen-Extra项目通过扩展java.time提供了额外的类。该项目是java.time可能未来添加内容的试验场。您可能会在这里找到一些有用的类,例如Interval, YearWeek, YearQuarter更多


如需更详细信息,请参见类似问题中我的答案

...并参见类似问题中我的答案


3
据我所知,以1月1日为日期的第一周是第1周。
这就是为什么12/31/2010会返回第1周。
尝试使用12/31/2011,您将得到第52周。

编辑:周是与地区相关的,有时定义为从星期日到星期六,有时定义为从星期一到星期日。

很可能是从财政年度的角度来看待它,这是一个以周一至周日为基础的视角。 - JasCav
1
应该补充说明,“一周的第一天”是与地区相关的。许多国家将星期一定义为一周的第一天,而不是美国。 - EboMike
1
不仅一周的开始是与地区相关的,而且如何确定一年的第一周也是如此。 - jarnbjo

2

这是因为周的起始时间与当地相关。

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

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


是的。然而,如果一周从星期一开始,同样的原则也适用。 - anon

1

java.time

正如被接受的答案中所指定的,一年中的周数是与Locale相关的。

演示:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.Locale;

class Main {
    public static void main(String[] args) {
        DateTimeFormatter parser = DateTimeFormatter.ofPattern("dd/MM/uuuu", Locale.ROOT);
        LocalDate date = LocalDate.parse("31/12/2010", parser);
        System.out.println(date.get(WeekFields.of(Locale.US).weekOfWeekBasedYear()));
        System.out.println(date.get(WeekFields.of(Locale.GERMANY).weekOfWeekBasedYear()));
    }
}

输出:

1
52

了解有关现代日期时间 API 的更多信息,请参阅 教程:日期时间


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接