WEEK_OF_YEAR在不同的机器上不一致

时间:2014-07-18 14:56:49

标签: java calendar jvm

更新:好吧,我似乎找到了答案的一半。如果我使用无参数getInstance创建我的日历,我得到WEEK_OF_YEAR = 52.但是,如果我通过向getInstance提供Local.getDefaul()创建它,我得到WEEK_OF_YEAR = 1.完全没有预料到这一点。 ..我想,需要重新阅读日历文档。

根据时间戳构建日历,该时间戳对应于星期六,2011年1月1日00:00:00 GMT

使用java.util.Date,Calendar和TimeZone的相同代码在不同的机器上具有不同的行为(具有相同的区域设置);除了WEEK_OF_YEAR之外,日历中的所有字段都是相同的。在我的机器上它是52(实际上在我的两台机器上)。在我的同事的机器上它是1(这似乎是正确的)。

import java.util.Date;
import java.util.TimeZone;
import java.util.Calendar;
import java.util.Locale;

public class CalendarTest {

    public static void main(String[] args) {

        Locale l = Locale.getDefault();
        System.out.println(l);
        Long d = new Long(1293840000000l);

        Calendar c = Calendar.getInstance();
        c.setTimeZone(TimeZone.getTimeZone("UTC"));
        c.setTime(new Date(d));

        System.out.println(c.toString());
}

.. locale是en_US,但Calendar是:

>java.util.GregorianCalendar[time=1293840000000,
areFieldsSet=true,
areAllFieldsSet=true,
lenient=true,
zone=sun.util.calendar.ZoneInfo[
id="UTC",
offset=0,
dstSavings=0,
useDaylight=false,
transitions=0,lastRule=null
],
firstDayOfWeek=2,
minimalDaysInFirstWeek=4,
ERA=1,
YEAR=2011,
MONTH=0,
WEEK_OF_YEAR=52,
WEEK_OF_MONTH=0,
DAY_OF_MONTH=1,
DAY_OF_YEAR=1,
DAY_OF_WEEK=7,
DAY_OF_WEEK_IN_MONTH=1,
AM_PM=0,HOUR=0,
HOUR_OF_DAY=0,
MINUTE=0,
SECOND=0,
MILLISECOND=0,
ZONE_OFFSET=0,
DST_OFFSET=0]

可能导致此WEEK_OF_YEAR不一致的原因是什么?

2 个答案:

答案 0 :(得分:4)

firstDayOfWeek& minimalDaysInFirstWeek

原来是一个功能,而不是一个bug。

您看到的不同行为的原因是问题中显示的输出中报告的两个设置:

  • firstDayOfWeek
  • minimalDaysInFirstWeek

阅读类和子类的文档非常重要:

第二个文档详细解释了上面列出的这两个设置对于确定本地周是如何至关重要的。

日历

请注意日历。 2011年的第一天是星期六。本月的第二个是星期日,星期日是美国的默认星期开始。

Calendar of month of January 2011 showing the first of the month is a Saturday

在设置为美国语言环境的Mac OS X计算机上,这些设置均为1。如果所需的最小天数为1,那么第一天将进入本地化的第1周.Java会报告此情况。

但是在您报告的问题机器上,这些设置分别为2和4。我不知道你是如何通过默认设置改变这些设置的,但是你做到了。

  • firstDayOfWeek | 12(周日与周一)
  • minimalDaysInFirstWeek | 14

最少4天意味着First在新的一年中没有资格作为一周。所以它是去年(2010年)的第52周。 2011年第一周是2011年1月2日至1月8日。

因此,在Java 7中java.util.Calendar类的文档中,您看到的行为符合预期。神秘的是这些设置是如何从问题机器上的默认设置中更改的?

ISO 8601

顺便说一句,该文件提到了2& 2的设置。 4给出了ISO 8601标准定义的行为,如my other answer中所述。这可能是为什么这些设置在您的问题机器上非默认设置的线索。有人,系统管理员或程序员,可能试图获得标准行为而不是本地化行为。

示例代码

让我们用一些代码来证明这一点。我们将使用问题代码的修改版本。我们这里的代码明确地设置了有争议的变量。因此,您可以在任何机器上运行此示例,正常或问题。首先,我们强制使用默认情况下在美国语言环境计算机1& 1。然后我们使用问题2&中报告的设置。 4

Locale l = Locale.getDefault();
System.out.println( l + "\n" );
Long d = new Long( 1293840000000l );

Calendar c = Calendar.getInstance();
c.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
c.setTime( new Date( d ) );

// Running Java 8 Update 11, Mac OS 10.8.5, virtual machine in Parallels 9, hosted on Mac with Mavericks.

// Force the use of default settings found on a machine set for United States locale (using Apple defaults).
c.setFirstDayOfWeek( 1 );
c.setMinimalDaysInFirstWeek( 1 );
// Reports: WEEK_OF_YEAR=1
System.out.println( "Default US settings:\n" + c.toString() + "\n" );

// Using reported settings (Coincides with ISO 8601 Week definition).
c.setFirstDayOfWeek( 2 );
c.setMinimalDaysInFirstWeek( 4 );
// Reports: WEEK_OF_YEAR=52
System.out.println( "Reported settings (ISO 8601):\n" + c.toString() + "\n" );

跑步时......

en_US

Default US settings:
java.util.GregorianCalendar[time=1293840000000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2011,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=1,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=0,DST_OFFSET=0]

Reported settings (ISO 8601):
java.util.GregorianCalendar[time=1293840000000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2011,MONTH=0,WEEK_OF_YEAR=52,WEEK_OF_MONTH=0,DAY_OF_MONTH=1,DAY_OF_YEAR=1,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=0,DST_OFFSET=0]

故事的道德

使用ISO 8601 standard weeks


感谢Marco13,他对该问题的评论引发了这个答案。

答案 1 :(得分:0)

[见下面的更新。答案不正确。]

对于{#3}},其中第1周有第一个星期四,ISO 8601 weeks 2010-W52

为了进行比较和实验,请尝试在Java 8中使用correct answer或新的Joda-Time包(受Joda-Time启发)。无论如何,你应该使用那些java.util.Date和.Calendar类是一个众所周知的麻烦混乱。

约达时间

DateTime dateTime = new DateTime( 2011, 1, 1, 0, 0, 0, DateTimeZone.UTC );
int weekNumber = dateTime.getWeekOfWeekYear();
String output = ISODateTimeFormat.weekDate().print( dateTime );

更新:回答不相关

我错误地认为java.util.Calendar按照ISO 8601标准确定了一周的年份。它不是。它使用Locale来影响其getFirstDayOfWeek()方法的结果。然后它使用从那天开始的最早的七天时段作为一年的第一周。 java.time

因此使用Joda-Time或java.time无助于调试问题。我留下这个答案来强调(a)使用标准周与本地周之间的区别,以及(b)不做假设的重要性。