简体   繁体   English

WEEK_OF_YEAR在不同的机器上不一致

[英]WEEK_OF_YEAR inconsistent on different machines

Update: ok, I seem to have found half the answer. 更新:好的,我似乎找到了答案的一半。 If I created my Calendar with a no-argument getInstance, I get WEEK_OF_YEAR = 52. However, if I create it with supplying Local.getDefaul() to the getInstance, I get WEEK_OF_YEAR = 1. Totally didn't expect this... need to re-read the Calendar docs, I guess. 如果我使用无参数getInstance创建我的日历,我得到WEEK_OF_YEAR = 52.但是,如果我通过向getInstance提供Local.getDefaul()创建它,我得到WEEK_OF_YEAR = 1.完全没想到这个...我想,需要重新阅读日历文档。

Building a Calendar from a timestamp, which corresponds to Sat, 01 Jan 2011 00:00:00 GMT . 从时间戳构建日历,对应于2011年1月1日星期六00:00:00 GMT

The same code, using java.util.Date, Calendar and TimeZone, is behaving differently on different machines (with the same locale); 使用java.util.Date,Calendar和TimeZone的相同代码在不同的机器上具有不同的行为(具有相同的区域设置); all the fields in the Calendar are the same, except WEEK_OF_YEAR. 除了WEEK_OF_YEAR之外,日历中的所有字段都是相同的。 On my machine it is 52 (on two of my machines, actually). 在我的机器上它是52(实际上在我的两台机器上)。 On my coworker's machines it's 1 (which seems to be correct). 在我的同事的机器上它是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 is en_US, but Calendar is: .. 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]

What might be causing this WEEK_OF_YEAR inconsistency? 可能导致WEEK_OF_YEAR不一致的原因是什么?

firstDayOfWeek & minimalDaysInFirstWeek firstDayOfWeekminimalDaysInFirstWeek

Turns out to be a feature, not a bug. 原来是一个功能,而不是一个bug。

The cause of the different behaviors you see is two settings reported in your output shown in the Question: 您看到的不同行为的原因是问题中显示的输出中报告的两个设置:

  • firstDayOfWeek
  • minimalDaysInFirstWeek

It's important to read the doc for both the class and subclass: 阅读类和子类的doc非常重要:

The second doc explains in detail how those two settings listed above are crucial to determining a localized week. 第二个文档详细解释了上面列出的这两个设置对于确定本地周是如何至关重要的。

Calendar 日历

Note the calendar. 请注意日历。 The first day of 2011 is a Saturday. 2011年的第一天是星期六。 The second of the month is a Sunday, and Sunday is the default start-of-week for United States. 本月的第二个是星期日,星期日是美国的默认星期开始日。

2011年1月的日历显示本月的第一个月是星期六

On a Mac OS X computer set to United States locale, these settings are both 1 . 在设置为美国语言环境的Mac OS X计算机上,这些设置均为1 If the minimum days needed is 1, then the First lands on a localized Week 1. Java reports this. 如果所需的最小天数为1,那么第一天就会出现在本地化的第1周.Java会报告这一点。

But on your reported problem machine, these settings are 2 and 4, respectively. 但是在您报告的问题机器上,这些设置分别为2和4。 I don't know how you got these settings altered from the usual defaults, but you did. 我不知道你是如何通过默认设置改变这些设置的,但是你做到了。

  • firstDayOfWeek | firstDayOfWeek | 1 versus 2 (Sunday versus Monday) 12 (周日对周一)
  • minimalDaysInFirstWeek | minimalDaysInFirstWeek | 1 versus 4 14

The minimum of 4 days means that the First does not qualify as a week in the new year. 至少4天意味着First不符合新年的一周。 So it is week 52 of the previous year (2010). 所以它是去年(2010年)的第52周。 The first week of 2011 is January 2, 2011 through January 8. 2011年第一周是2011年1月2日至1月8日。

So the behavior you are seeing matches expectations given the documentation for the java.util.Calendar class in Java 7. The mystery is how did those settings get changed away from the default on your problem machine? 因此,给出Java 7中java.util.Calendar类的文档时,您看到的行为符合预期。神秘的是这些设置是如何从问题机器上的默认设置中更改的?

ISO 8601 ISO 8601

By the way, the doc mentions that settings of 2 & 4 gives you the behavior defined by the ISO 8601 standard, as mentioned in my other answer . 顺便说一下,文档提到2和4的设置为您提供了ISO 8601标准定义的行为,如我的其他答案中所述 That may be the clue as to why these settings are non-default on your problem machine. 这可能是为什么这些设置在您的问题机器上非默认设置的线索。 Someone, a sysadmin or programmer, may be trying to get standard behavior rather than localized behavior. 某人,系统管理员或程序员,可能试图获得标准行为而不是本地化行为。

Example Code 示例代码

Let's demonstrate this with some code. 让我们用一些代码来证明这一点。 We'll use a modified version of the code from the Question. 我们将使用问题代码的修改版本。 Our code here explicitly sets the variables at issue. 我们这里的代码明确地设置了有争议的变量。 So you can run this example on any of your machines, normal or problem. 因此,您可以在任何机器上运行此示例,正常或问题。 First we force the use of the settings found by default on a US Locale machine, 1 & 1 . 首先,我们强制使用默认情况下在美国语言环境机器11上找到的设置。 Then we use the settings reported in the Question, 2 & 4 . 然后我们使用问题24报告的设置。

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" );

When run… 跑的时候......

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]

Moral Of The Story 故事的道德启示

Use ISO 8601 standard weeks ! 使用ISO 8601标准周 😉 😉


Thanks to Marco13, whose comments on the Question sparked this answer. 感谢Marco13,他对该问题的评论引发了这个答案。

[See Update below. [见下面的更新。 Incorrect answer.] 错误答案。]

For ISO 8601 weeks where week # 1 has the first Thursday, the correct answer 2010-W52 . 对于ISO 8601周 ,第1周有第一个星期四, 正确答案是 2010-W52

For comparison and experiment, try using either Joda-Time or the new java.time package in Java 8 (inspired by Joda-Time). 为了进行比较和实验,请尝试使用Joda-Time或Java 8中的新java.time包(受Joda-Time启发)。 And anyways, you should be using those as the java.util.Date and .Calendar classes are a notoriously troublesome mess. 无论如何,你应该使用那些java.util.Date和.Calendar类是一个众所周知的麻烦混乱。

Joda-Time 乔达时间

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

UPDATE: Answer Irrelevant 更新:回答不相关

I assumed incorrectly that java.util.Calendar determined week-of-year by the ISO 8601 standard. 我错误地认为java.util.Calendar按照ISO 8601标准确定了一周的年份。 It does not. 它不是。 It uses a Locale to influence the result of its getFirstDayOfWeek() method. 它使用Locale来影响其getFirstDayOfWeek()方法的结果。 It then uses the earliest seven day period beginning with that day as its first week of year. 然后它使用从那天开始的最早的七天时段作为一年的第一周。 See "First Week" section of doc . 请参阅doc的“第一周”部分

So using Joda-Time or java.time will not help in debugging the problem. 所以使用Joda-Time或java.time无助于调试问题。 I leave this answer to highlight (a) the difference between using a standard week versus a localized week, and (b) the importance of not making assumptions. 我留下这个答案来强调(a)使用标准周与本地周之间的区别,以及(b)不做假设的重要性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM