5

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.

Building a Calendar from a timestamp, which corresponds to Sat, 01 Jan 2011 00:00:00 GMT.

The same code, using java.util.Date, Calendar and TimeZone, is behaving differently on different machines (with the same locale); all the fields in the Calendar are the same, except WEEK_OF_YEAR. On my machine it is 52 (on two of my machines, actually). On my coworker's machines it's 1 (which seems to be correct).

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:

>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?

alexakarpov
  • 1,848
  • 2
  • 21
  • 39
  • See the machine time of coworker's machine if it make the difference ? – sjain Jul 18 '14 at 15:00
  • 1
    Information like the Java version and the operating systems (+version) might be interesting (it *should* not be relevant, but am not sure whether they *are* not relevant...) – Marco13 Jul 18 '14 at 15:06
  • 1
    My guess: One of the computers is using Java 8, and the other is using Java 7. Is this correct? – Marco13 Jul 18 '14 at 15:16
  • Regardless of that my previous comment: The output says `firstDayOfWeek=2`, which is "unusual". I tested this with `Locale.US` and *all* available `TimeZone`s (both on Java7 and Java8), and there is no combination where it says `firstDayOfWeek=2` for any `Locale.US`. Again, some more information about the actual setup may be helpful here (and possibly an easy-to-compare version of the output that *you* received - it will almost certainly say `firstDayOfWeek=1`... but I did not yet manage to figure out where the difference might come from....) – Marco13 Jul 18 '14 at 15:44
  • Apologies for the comment chain, but you should have a look at http://docs.oracle.com/javase/8/docs/api/java/util/spi/LocaleServiceProvider.html and try to use different providers by passing different arguments to `java.locale.providers=SPI,JRE`. It seems that the behavior (that is, the default lookup order) changed for Java8, but now can be configured. `java.locale.providers=CLDR,JRE` might be worth a try. – Marco13 Jul 18 '14 at 16:07
  • $ java -version java version "1.7.0_65" Java(TM) SE Runtime Environment (build 1.7.0_65-b17) Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode) – alexakarpov Jul 18 '14 at 16:28
  • all the machines are on Java 7; plus, I would be very surprised if Java had this backward incompatibility with 7... – alexakarpov Jul 18 '14 at 16:29
  • all the machines in questions are running Mac Os X, 10.9.4 on mine; not 100% sure about my coworkers', but most likely the same. – alexakarpov Jul 18 '14 at 16:30
  • NOTE: question is updated; apparently, Calendar's default constructor messes something up, and is not using the Locale.getDefault – alexakarpov Jul 18 '14 at 17:04
  • @alexakarpov Typo? In your "Update" at top of Question, did you mean `WEEK_OF_YEAR` rather than `DAY_OF_WEEK`? – Basil Bourque Jul 18 '14 at 21:10

2 Answers2

6

firstDayOfWeek & minimalDaysInFirstWeek

Turns out to be a feature, not a 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:

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. The second of the month is a Sunday, and Sunday is the default start-of-week for United States.

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

On a Mac OS X computer set to United States locale, these settings are both 1. If the minimum days needed is 1, then the First lands on a localized Week 1. Java reports this.

But on your reported problem machine, these settings are 2 and 4, respectively. I don't know how you got these settings altered from the usual defaults, but you did.

  • firstDayOfWeek | 1 versus 2 (Sunday versus Monday)
  • minimalDaysInFirstWeek | 1 versus 4

The minimum of 4 days means that the First does not qualify as a week in the new year. So it is week 52 of the previous year (2010). The first week of 2011 is January 2, 2011 through January 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?

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. 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. Then we use the settings reported in the Question, 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" );

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!


Thanks to Marco13, whose comments on the Question sparked this answer.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • thank you for the detailed answer. Gosh, Calendars are really a complicated business.. – alexakarpov Jul 18 '14 at 23:41
  • PS: also, this explains why nuking and re-installing the whole Mac Os X made the difference (as suggested by a team member - since who knows what was done to that OS before) – alexakarpov Jul 19 '14 at 00:14
  • 1
    PPS: now I am trying to figure out _what_ is messing up with my JVM calendar settings. I've created a new topic/question... the worst part of the problem seems to be that 'Locale.setDefault(Locale.getDefault())' is _resolving_ the problem - but it's ridiculous and wrong... – alexakarpov Jul 19 '14 at 18:00
0

[See Update below. Incorrect answer.]

For ISO 8601 weeks where week # 1 has the first Thursday, the correct answer 2010-W52.

For comparison and experiment, try using either Joda-Time or the new java.time package in Java 8 (inspired by Joda-Time). And anyways, you should be using those as the java.util.Date and .Calendar classes are a notoriously troublesome mess.

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. It does not. It uses a Locale to influence the result of its getFirstDayOfWeek() method. It then uses the earliest seven day period beginning with that day as its first week of year. See "First Week" section of doc.

So using Joda-Time or java.time will not help in debugging the problem. 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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • thanks for the suggestion; I'm third day at the new job, and so it may not be the best time for me to suggest introducing new dependencies; plus, it's "working" for everyone else... so this is more for me to understand what's going on, than to solve some pressing problem.. – alexakarpov Jul 18 '14 at 16:34
  • 1
    @alexakarpov Adding Joda-Time to the project would be an *excellent* way to cement your reputation on a new job. ;-) The j.u.Date/Calendar classes really are that bad. But in the short-term my suggestion is merely **use Joda-Time for comparison** as part of your investigation. If the same version of Joda-Time works consistently in both environments (or not), that will provide you with more information. As other suggested, the problem may be current time setting or time zone on machine, bug in j.u.Date/Calendar showing in different versions, outdated tz database, or others. – Basil Bourque Jul 18 '14 at 19:32
  • I agree; but the most immediate goal is to find out why the Calendar and default locale get broken.. – alexakarpov Jul 19 '14 at 18:02