1

This is a question related to Java Date year calculation is off by year for two days

I understand the problem appeared from using 'YYYY' instead of 'yyyy', whereby 'YYYY' refers to calendar year instead of the actual year, resulting in the year being wrong if the dates fell onto the first week of January's calendar year.

I tried to read further and understand the problem in

https://docs.oracle.com/javase/9/docs/api/java/util/GregorianCalendar.html#week_year

And it says

"A week year is in sync with a WEEK_OF_YEAR cycle. All weeks between the first and last weeks (inclusive) have the same week year value. Therefore, the first and last days of a week year may have different calendar year values."

I have been trying to see if there are any time of the year where 01-Jan-XXXX is actually displayed as 01-Jan-(XXXX-1) but have not managed to find any. Is there a case where this may happen?

I did something simple to take string dates and print out using YYYYMMdd format

public static void main(String[] args) throws ParseException
{
    Calendar testCalendar = Calendar.getInstance();
    System.out.println("First day of the week: " + testCalendar.getFirstDayOfWeek());
    System.out.println("Minimal Days in First Week: " + testCalendar.getMinimalDaysInFirstWeek());
    SimpleDateFormat YYYYMMdd= new SimpleDateFormat("YYYYMMdd");
    String dateString = "01/01/2016";
    Date date = new Date();
    date = new SimpleDateFormat("dd/MM/yyyy").parse(dateString);
    testCalendar.setTime(date);
    int week = testCalendar.get(Calendar.WEEK_OF_YEAR);
    String date2 = YYYYMMdd.format(date);
    System.out.println("Week Number: " + week);
    System.out.println("Date: " + date2);

}

And the output was

First day of the week: 1

Minimal Days in First Week: 1

Week Number: 1

Date: 20161231

If I change the date to "01/01/2016"

The output was

First day of the week: 1

Minimal Days in First Week: 1

Week Number: 1

Date: 20160101

So 01/01/2016 is the first week of of 2016, and not week 53 of 2015.

Alex Sim
  • 617
  • 1
  • 6
  • 13
  • Any particular reason why you are still using the long outdated data and time classes `Calendar`, `SimpleDateFormat` and `Date`? [`java.time`, the modern Jav date and time API](https://docs.oracle.com/javase/tutorial/datetime/), is so much nicer to work with. – Ole V.V. Jan 10 '18 at 08:47
  • 1
    Hi, I am working on an existing project that has very old codes from the past. This issue just occurred because of 31/12/2017 showing as 31/12/2018 in UI. So I'm just trying to understanding it better. They aren't looking to make much changes until they want to overhaul the system. I will take note of what you have shared when we plan to re-do the system. – Alex Sim Jan 10 '18 at 08:50

3 Answers3

1

No. The week year is only to be used in conjunction with the week. For example the 1st January 2016 (a Friday) is in the 53. week of 2015. It will never be displayed as 1. January 2015 since that would be ambiguous.

Calendar d = Calendar.getInstance();
d.set(Calendar.YEAR, 2016)
d.set(Calendar.MONTH, Calendar.JANUARY);
d.set(Calendar.DATE, 1);

SimpleDateFormat ft = new SimpleDateFormat("w-YYYY");
ft.format(d.getTime()); 
// => "53-2015"
Martin Schneider
  • 3,268
  • 4
  • 19
  • 29
  • It *would* be displayed as `01-Jan-2015` if the format string incorrectly used `YYYY` instead of `yyyy`, which I think is what question was asking for. – Andreas Jan 10 '18 at 07:59
  • Together with the initial question I think what the OP was asking for is "is there any valid use case to display 01-Jan-XXXX as 01-Jan-(XXXX-1)" – Martin Schneider Jan 10 '18 at 08:01
  • Be aware that this depends on your locale. Not all locales use ISO week numbers. In particular, if you set the locale to `Locale.ENGLISH` the example will return `1-2016`. – Marten Jan 10 '18 at 08:03
  • I believe OP is trying to understand `YYYY`. --- Oh, and `2016-01-01` is week 1 of 2016 in `Locale.US`. – Andreas Jan 10 '18 at 08:04
  • Hi, thanks for all the very quick and helpful answer. I'm not looking at any valid use case to display as (XXXX-1), but was just confused by the statement made in bold. It seemed to be stating that there MAY be a time where it's supposed to display XXXX, but would display XXXX-1 instead. I re-read the item in bold and I guess what it is trying to say that the first day of the last week and the last day of the last week may contain different calendar year. I believe I got confused by my own english. – Alex Sim Jan 10 '18 at 08:26
  • @Andreas I did a test and it did display as 01-Jan-2016 and not 01-Jan-2015 though. I edited my main topic. – Alex Sim Jan 10 '18 at 08:49
  • @AlexSim Did you read [my answer](https://stackoverflow.com/a/48182916/5221149)? If you are in the US, January 1 will always be in week 1 *(it's the definition)*, so the week-year will always been same as calendar year. – Andreas Jan 10 '18 at 08:53
  • @Andreas Yes I have read it. I just got slightly confused by your comment when you said " It would be displayed as 01-Jan-2015 if the format string incorrectly used YYYY instead of yyyy, which I think is what question was asking for" Much thanks for the clarification – Alex Sim Jan 10 '18 at 09:00
1

In the US, the first week of the year is defined as being the week containing January 1*.

As a consequence, the week-year for January 1 will always be the same as the calendar year, in the US.

*https://en.wikipedia.org/wiki/Week#Week_numbering

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Correct answer. The “in the US” part is decisive. For ISO weeks (used roughly in the rest of the world) the answer is different, here Jan 1 has the previous year as week year in approx. 3 out of 7 cases, namely when the year begins on a Friday, Saturday or Sunday. – Ole V.V. Jan 10 '18 at 08:16
1

For a concrete answer, the following table shows January 1 for each year from 2010 through 2020 with day-of-week, week-based year and week number in ISO (the international standard) and in the US.

Year  DOW  ISO      US
2010  Fri  2009-53  2010-01
2011  Sat  2010-52  2011-01
2012  Sun  2011-52  2012-01
2013  Tue  2013-01  2013-01
2014  Wed  2014-01  2014-01
2015  Thu  2015-01  2015-01
2016  Fri  2015-53  2016-01
2017  Sun  2016-52  2017-01
2018  Mon  2018-01  2018-01
2019  Tue  2019-01  2019-01
2020  Wed  2020-01  2020-01

As you can see, internationally January 1 regularly falls in week 52 or 53 of the previous year, while in the US it always falls in week 1 of its own year.

International rule: Week 1 is the first one that contains at least 4 days of the new year. In other words, a week belongs in the year where most of its days are. In yet other words, week 1 is the one that holds the first Thursday of the year (since weeks begin on Mondays). This implies that when January 1 is a Friday, Saturday or Sunday, it belongs to the last week of the previous year.

US rule: Week 1 is the week that contains January 1. That January 1 always falls in week 1 of its own year follows from this definition (weeks begin on Sundays).

The table came out of this snippet:

    System.out.println("Year  DOW  ISO      US");
    for (int year = 2010; year <= 2020; year++) {
        LocalDate jan1 = LocalDate.of(year, Month.JANUARY   , 1);
        System.out.format(Locale.ROOT, "%4d  %3s  %4d-%02d  %4d-%02d%n",
                year, jan1.getDayOfWeek().getDisplayName(TextStyle.SHORT_STANDALONE, Locale.ROOT), 
                jan1.get(WeekFields.ISO.weekBasedYear()), jan1.get(WeekFields.ISO.weekOfWeekBasedYear()), 
                jan1.get(WeekFields.SUNDAY_START.weekBasedYear()), jan1.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear()));
    }

I am of course using java.time, the modern Java date and time API. I warmly recommend it over the outdated Calendar, SimpleDateFormat and Date.

Valentin Michalak
  • 2,089
  • 1
  • 14
  • 27
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    This is very useful information and I will take note to use java.time when we re-vamp the codes to this project. This project has 10 years old codes that are very outdated. – Alex Sim Jan 10 '18 at 09:01
  • 1
    @AlexSim Any new or revised code you author can use the java.time classes while still interoperating with the old code. Call on the new conversion methods added to the old legacy classes. You can convert back-and-forth between the modern and legacy objects with ease. For example: `java.util.Date::toInstant()` – Basil Bourque Jan 11 '18 at 21:41