簡體   English   中英

WEEK_OF_YEAR在不同的機器上不一致

[英]WEEK_OF_YEAR inconsistent on different machines

更新:好的,我似乎找到了答案的一半。 如果我使用無參數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不一致的原因是什么?

firstDayOfWeekminimalDaysInFirstWeek

原來是一個功能,而不是一個bug。

您看到的不同行為的原因是問題中顯示的輸出中報告的兩個設置:

  • firstDayOfWeek
  • minimalDaysInFirstWeek

閱讀類和子類的doc非常重要:

第二個文檔詳細解釋了上面列出的這兩個設置對於確定本地周是如何至關重要的。

日歷

請注意日歷。 2011年的第一天是星期六。 本月的第二個是星期日,星期日是美國的默認星期開始日。

2011年1月的日歷顯示本月的第一個月是星期六

在設置為美國語言環境的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和4的設置為您提供了ISO 8601標准定義的行為,如我的其他答案中所述 這可能是為什么這些設置在您的問題機器上非默認設置的線索。 某人,系統管理員或程序員,可能試圖獲得標准行為而不是本地化行為。

示例代碼

讓我們用一些代碼來證明這一點。 我們將使用問題代碼的修改版本。 我們這里的代碼明確地設置了有爭議的變量。 因此,您可以在任何機器上運行此示例,正常或問題。 首先,我們強制使用默認情況下在美國語言環境機器11上找到的設置。 然后我們使用問題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" );

跑的時候......

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標准周 😉


感謝Marco13,他對該問題的評論引發了這個答案。

[見下面的更新。 錯誤答案。]

對於ISO 8601周 ,第1周有第一個星期四, 正確答案是 2010-W52

為了進行比較和實驗,請嘗試使用Joda-Time或Java 8中的新java.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()方法的結果。 然后它使用從那天開始的最早的七天時段作為一年的第一周。 請參閱doc的“第一周”部分

所以使用Joda-Time或java.time無助於調試問題。 我留下這個答案來強調(a)使用標准周與本地周之間的區別,以及(b)不做假設的重要性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM