This question already has answers here:
Calendar.Month gives wrong output
(4 answers)
Closed 2 years ago.
Why does calendar.WEEK_OF_YEAR prints 3 instead of 6.
GregorianCalendar calendar = new GregorianCalendar();
System.out.println("time"+calendar.getTime());
System.out.println("week of year "+calendar.WEEK_OF_YEAR);
Output
time Tue Feb 09 12:58:02 IST 2021
week of year 3
tl;dr
LocalDate.now().get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )
February 9, 2021 is in week # 6 of the ISO 8601 week-based year of 2021 that runs from Monday January 4, 2021 through Sunday January 2, 2022 (inclusive).
In standard notation, Feb 9, 2021 is the second day Tuesday of the sixth week of the week-based year 2021: 2021-W06-2
Details
The terrible class GregorianCalendar was supplanted years ago by the modern java.time classes defined in JSR 310.
java.time
Get current date.
LocalDate today = LocalDate.now( ZoneId.of( "Asia/Kolkata" ) ) ;
Get the week of a week-based year, using the definition of ISO 8601. In this definition:
a week runs Monday-Sunday, and
week # 1 contains the first Thursday of the calendar-year.
As a consequence of that definition, each week-based year has either 52 or
53 whole weeks.
int week = today.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
And get the year number of the week-based year for that date. This may differ from the calendar year number in the first and last weeks.
int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR );
For example, the ISO 8601 week-based year for 2021 started January 4, 2021. February 9, 2021 is in week # 6.
See this list of weeks in ISO 8601 week-based year of 2021.
calendar.WEEK_OF_YEAR is a constant, you don't use it like that.
You probably meant calendar.get(Calendar.WEEK_OF_YEAR) (and, today February the 9th it will print 7, not 6, because it's 1-based, not 0-based).
Note that, as the nice answer from Basil Bourque explains, you should use the new DateTime API.
The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.
For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7.
If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
The answer by Basil Bourque nicely explains how to use the Java SE 8 date-time API. If you still want to use the legacy API, you have a couple of options:
import java.text.SimpleDateFormat;
import java.util.Calendar;
class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// Getting current week using SimpleDateFormat
System.out.println("Current week: " + new SimpleDateFormat("w").format(calendar.getTime()));
// Getting current week using Calendar.WEEK_OF_YEAR
System.out.println("Current week: " + calendar.get(Calendar.WEEK_OF_YEAR));
}
}
Output:
Current week: 6
Current week: 6
The ISO 8601 definition for week 01 is the week with the first Thursday of the Gregorian year (i.e. of January) in it. With this definition, week 01 in 2021 starts on 4th January and therefore the current week is the 6th week.
Related
This question already has answers here:
Android Calendar illegalArgumentException when calendar.month set to 1
(2 answers)
Closed 3 years ago.
import java.util.Calendar;
public class WeekYear {
static String input = "202001";
//static String format = "YYYYMM";
public static void main(String[] args) throws ParseException {
Calendar lCal = Calendar.getInstance();
System.out.println(lCal.isLenient());
lCal.setLenient(false);
lCal.set(Calendar.YEAR, new Integer(input.substring(0, 4)).intValue());
lCal.set(Calendar.WEEK_OF_YEAR, new Integer(input.substring(4, 6)).intValue());
//lCal.setMinimalDaysInFirstWeek(5);
System.out.println(lCal.isLenient());
System.out.println(lCal.getTime());
//lCal.set(Calendar.YEAR, new Integer(input.substring(0, 4)).intValue());
//lCal.set(Calendar.WEEK_OF_YEAR, new Integer(input.substring(4, 6)).intValue());
//System.out.println(lCal.getTime());
}
}
When this code is executed on Nov 22nd, 2020 I get an IllegalArgumentException from Calendar.getTime(). But when executed on Nov 27, 2020 it works fine.
The documentation says:
The setLenient(boolean leniency) method in Calendar class is used to specify whether the interpretation of the date and time is to be lenient or not. Parameters: The method takes one parameter leniency of the boolean type that refers to the mode of the calendar.
Any explanation? I am not able to reproduce the issue even in my local now. Local time is set to CST
Exception Stack:
Exception in thread "main" java.lang.IllegalArgumentException: year: 2020 -> 2019
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at java.util.Calendar.getTime(Calendar.java:1755)
at WildDog.main(WildDog.java:13)
`````````
tl;dr
Never use Calendar, now legacy, supplanted by java.time classes such as ZonedDateTime.
Use a purpose-built class, YearWeek from the ThreeTen-Extra project, to track standard ISO 8601 weeks.
Custom formatter
Define a DateTimeFormatter object to match your non-standard input string.
org.threeten.extra.YearWeek
.parse(
"202001" ,
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue( IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
.toFormatter()
)
.toString()
2020-W01
Standard formatter
Or manipulate your input string to comply with the ISO 8601 standard format, inserting a -W in the middle between the week-based-year and the week. The java.time classes and the ThreeTen-Extra classes all use the ISO 8601 formats by default when parsing/generating strings.
String input = "202001";
String inputModified = input.substring( 0 , 4 ) + "-W" + input.substring( 4 );
YearWeek yearWeek = YearWeek.parse( inputModified ) ;
yearWeek.toString(): 2020-W01
Avoid legacy date-time classes
Do not waste your time trying to understand Calendar. This terrible class was supplanted years ago by the modern java.time classes defined in JSR 310.
Definition of week
You must specify your definition of a week. Do you mean week number 1 contains the first day of the year? Or week # 1contains a certain day of the week? Or week # 1 is the first calendar week to consist entirely of dates in the new year? Or perhaps an industry-specific definition of week? Some other definition?
One of the confusing things about Calendar is that its definition of a week shifts by Locale. This one of many reasons to avoid that legacy class.
Week-based year
Depending on your definition of week, the year of a week may not be the calendar year of some dates on that week. A week-based year may overlap with calendar years.
Standard weeks and week-based year
For example, the standard ISO 8601 week defines a week as:
Starting on Monday, and
Week # 1 contains the first Thursday of the calendar year.
So there are 52 or 53 whole weeks in every week-based year. Of course, that means some dates from the previous and/or following calendar years may appear in the first/last weeks of our week-based year.
org.threeten.extra.YearWeek
One problem is that you are trying to represent a year-week with a class that represents a moment, a date with time of day in the context of a time zone.
Instead, use a purpose-built class. You can find one in the ThreeTen-Extra library, YearWeek. This library extends the functionality of the java.time classes built into Java 8 and later.
With that class I would think that we could define a DateTimeFormatter to parse your input using the formatting pattern YYYYww where the YYYY means a 4-digit year of week-based-year, and the ww means the two-digit week number. Like this:
// FAIL
String input = "202001" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "YYYYww" ) ;
YearWeek yearWeek = YearWeek.parse( input , f ) ;
But using that formatter throws an DateTimeParseException for reasons that escape me.
Exception in thread "main" java.time.format.DateTimeParseException: Text '202001' could not be parsed: Unable to obtain YearWeek from TemporalAccessor: {WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2020},ISO of type java.time.format.Parsed
…
Caused by: java.time.DateTimeException: Unable to obtain YearWeek from TemporalAccessor: {WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2020},ISO of type java.time.format.Parsed
…
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: WeekBasedYear
Alternatively, we can use DateTimeFormatterBuilder to build up a DateTimeFormatter from parts. By perusing the OpenJDK source code for Java 13 for DateTimeFormatter.ISO_WEEK_DATE I was able to cobble together this formatter that seems to work.
DateTimeFormatter f =
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue( IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
.toFormatter()
;
Using that:
String input = "202001" ;
YearWeek yearWeek = YearWeek.parse( input , f ) ;
ISO 8601
Educate the publisher of your data about the ISO 8601 standard defining formats for representing date-time values textually.
To generate a string in standard format representing the value of our YearWeek, call toString.
String output = yearWeek.toString() ;
2020-W01
And parsing a standard string.
YearWeek yearWeek = YearWeek.parse( "2020-W01" ) ;
Basil Bourque has already provided a very good answer. Here’s one that doesn’t require an external dependency (provided you are using Java 8 or later).
java.time
WeekFields wf = WeekFields.of(Locale.US);
DateTimeFormatter yearWeekFormatter = new DateTimeFormatterBuilder()
.appendValue(wf.weekBasedYear(), 4)
.appendValue(wf.weekOfWeekBasedYear(), 2)
.parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.SUNDAY.getValue())
.toFormatter();
String input = "202001";
LocalDate date = LocalDate.parse(input, yearWeekFormatter);
System.out.println(date);
Output is:
2019-12-29
Assuming American weeks where Sunday is the first day of the week and week 1 is the week containing January 1, this is correct: week 1 of 2020 begins on Sunday, December 29, 2019. If you want weeks defined in some other way, just use a different WeekFields object.
I recommend that you don’t use the Calendar class. That class was always poorly designed and is now long outdated. Instead I am using java.time, the modern Java date and time API.
Any explanation?
With thanks to user85421 for how to reproduce. You are first creating a Calendar object (really an instance of GregorianCalendar) representing the current day, in your example Nov 22nd, 2020, a Sunday (apparently having set your computer clock nearly a year ahead). You are then setting its year to 2020 (no change) and its week number to 1. However, as we saw above, this would change the date to December 29, 2019, and thus create a conflict with the year that you set to 2020. Therefore GregorianCalendar decides that you are asking the impossible and throws the exception. The stack trace that I got was:
java.lang.IllegalArgumentException: YEAR: 2020 -> 2019
at java.base/java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2826)
at java.base/java.util.Calendar.updateTime(Calendar.java:3395)
at java.base/java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at java.base/java.util.Calendar.getTime(Calendar.java:1755)
at ovv.misc.Test.main(Test.java:17)
In your second example you were running your program on Nov 27, 2020, a Friday. This time the date is changed to Friday, January 3, 2020, so still within year 2020, and therefore there is no conflict and hence no exception.
The explanation presumes that your default locale is one where week 1 of the year is defined as the week that contains January 1. I have ran your code in my own locale after setting my computer’s time to Nov 22, 2020, and my time zone to America/Chicago. No exception was seen (output included Sun Jan 05 13:54:27 CST 2020). My locale follows the international standard, ISO. Monday is the first day of the week, and week 1 is the first week that has at least 4 days of the new year in it. So week 1 of 2020 is from Monday, December 30, 2019, through Sunday, January 5. I suppose that on a Monday or Tuesday I could reproduce your problem in this locale too, I haven’t tried.
PS How to parse an integer
Just a tip, to parse a string into an int, just use Integer.parseInt(yourString). No need to create a new Integer object.
Link
Oracle tutorial: Date Time explaining how to use java.time.
By the following way I set date object to calendar instance.
But when I get the week of year , wrongly it returns week of the year as 1.
Same as week of the year, week of the month value also wrong. It return as 6. But it should be 5.
Following is output of calendar instance value
java.util.GregorianCalendar[time=1514678400000,
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=2017, MONTH=11, WEEK_OF_YEAR=1, WEEK_OF_MONTH=6, DAY_OF_MONTH=31, DAY_OF_YEAR=365, DAY_OF_WEEK=1, DAY_OF_WEEK_IN_MONTH=5, AM_PM=0, HOUR=0, HOUR_OF_DAY=0, MINUTE=0, SECOND=0, MILLISECOND=0, ZONE_OFFSET=0, DST_OFFSET=0]
firstDayOfWeek=1 means that Sunday is the first day of the week. So Sunday December 31, 2017 belonged to week 1 of 2018.
It’s not well documented, but it turns out that the week of month follows the month, so on Dec 31, it gives you the number of the week in December. Since December 1 and 2 were Friday and Saturday, they formed week 1 of December. Week 2 began on December 3. So week 6 began on December 31 and lasted only that day. This explains why week of month is 6 in your case.
Whether we use IST time zone (I am assuming it means Asia/Kolkata) or UTC doesn’t make a difference. At 5:30 AM in India, it was 00:00 in UTC, the date was still December 31, and therefore the week numbers are also unchanged.
Your Calendar’s settings of first day of week and minimal days in first week follow the standard used in USA and some other countries. If instead you wanted the international standard where Monday is the first day of the week and there is a minimum of 4 days in week one, you may instantiate it with a locale of a country that uses the international standard, for example:
Calendar c = Calendar.getInstance(Locale.FRANCE);
In this case week of year will be 52 and week of month will be 4. Not 5. This is because the first three days of December are not enough to form a week, so week 1 of December begins on December 4. Then the first three days of December are in week 0.
Prefer to use java.time
All of that said, the Calendar class is long outmoded and poorly designed. I recommend you use java.time, the modern Java date and time API, instead. Specifically, the LocalDate & WeekFields classes.
For example:
LocalDate date = LocalDate.of(2017, Month.DECEMBER, 31);
System.out.println("ISO week of year " + date.get(WeekFields.ISO.weekOfWeekBasedYear()));
System.out.println("ISO week of month " + date.get(WeekFields.ISO.weekOfMonth()));
System.out.println("US week of year " + date.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear()));
System.out.println("US week of month " + date.get(WeekFields.SUNDAY_START.weekOfMonth()));
Output:
ISO week of year 52
ISO week of month 4
US week of year 1
US week of month 6
The results agree with those we got from Calendar.
Link: Oracle tutorial: Date Time explaining how to use java.time.
This started as a simple error: I had YYYY instead of yyyy in my format string for a SimpleDateFormat object. But I'm totally baffled by the results of my tests with the incorrect format string.
This code:
#Test
public void whatTheHell() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/YYYY");
Date d1 = sdf.parse("01/07/2016");
Date d2 = sdf.parse("02/08/2016");
Date d3 = sdf.parse("11/29/2027");
System.out.println(d1.toString());
System.out.println(d2.toString());
System.out.println(d3.toString());
} catch (ParseException pe) {
fail("ParseException: " + pe.getMessage());
}
}
produces this output:
Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2026
I've read the documentation on the 'Y' parameter here: https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html, but I still can't see the logic that's working here. Particularly the last instance: I can kinda-sorta understand how the dates in January (& maybe February) can be translated into December of the previous year, but moving the date of November 29th backwards by 11 months baffles me. And what's so special about December 27th?
Can anyone explain?
MORE INFORMATION
#Jan suggested that relying on the toString() method could be a problem, so I defined a date format to print YYYY MM dd '-' yyyy MM dd in the same code as above. Here is the additional output:
2016 12 27 - 2015 12 27
2016 12 27 - 2015 12 27
2027 12 27 - 2026 12 27
It's simple: December 27 2015 is day 1 of week 1 of week-year 2016 (and December 27 2026 is day 1 of week 1 of week-year 2027). This can be verified by adding these lines:
SimpleDateFormat odf = new SimpleDateFormat("YYYY-ww-u");
System.out.println(odf.format(d1));
System.out.println(odf.format(d2));
System.out.println(odf.format(d3));
If a SimpleDateFormat outputs a date it can use all fields: year, month, day, day of week, week of month, week in year, week-year etc.
On parsing, SimpleDateFormat expects a matching set of values: either day, month, year or day of week, week in year, week-year. Since you supplied a week-year but did not supply day of week and week in year, those to values have been assumed as 1.
The actual values depend on your locale:
which week of a year is week 1
which day is the first day of the week
(see https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html#week_and_year)
On my system (using de-ch locale, with "EEE MMM dd HH:mm:ss zzz yyyy - YYYY-ww-u" as format) I get
Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1
Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1
Mo Jan 04 00:00:00 MEZ 2027 - 2027-01-1
Definitions vary
A week can be defined in different ways. For example, in the United States, the first day of the week is considered to be Sunday most often while in Europe and many other places the first day is Monday.
Likewise, the week of a week-based year can also be defined in different ways.
Week # 1 contains January 1.
Week # 1 is the first week to contain a particular day-of-week such as Sunday.
Week # 1 is the first week to contain only days of the new year, with no dates from the previous year.
… and more
The legacy classes you are using implicitly use the definitions specified by a Locale. The locale being applied is also implicit, using the JVM’s current default Locale if you do not otherwise specify.
For even more interesting details about the difficulty in defining a week, see this Question, Different behavior of WeekFields on JVM 8 and JVM 10
ISO 8601
There is a practical international standard for date-time handling, ISO 8601.
The ISO 8601 definition of a week is:
Weeks start on a Monday
Week # 1 has the first Thursday of the calendar year
A year consists of either 52 or 53 whole weeks.
A year may have one or more days from the previous calendar year, and also from the following calendar year.
I suggest using the ISO 8601 standard definition whenever possible. This standard definition is simple and logical, with increasing adoption across industries.
java.time
The java.time classes offer some support for week of week-based year in the WeekFields class.
LocalDate ld = LocalDate.of( 2019 , Month.JANUARY , 1 ) ;
long week = ld.get( WeekFields.ISO.weekOfWeekBasedYear() ) ;
See this code run live at IdeOne.com.
ld.toString(): 2019-01-01
week: 1
org.threeten.extra.YearWeek
But if doing much of this work, I suggest adding the ThreeTen-Extra library to your project. You will find the YearWeek class to be helpful. This class offers several handy methods such as generating a LocalDate for any day within that week.
LocalDate ld = LocalDate.of ( 2019 , Month.JANUARY , 1 );
YearWeek yw = YearWeek.from ( ld );
LocalDate startOfWeek = yw.atDay ( DayOfWeek.MONDAY );
ld.toString(): 2019-01-01
yw.toString(): 2019-W01
startOfWeek.toString(): 2018-12-31
Notice how the first day of the year in the week-based year of 2019 is a date from the previous calendar year, 2018 rather than 2019.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
For instance:
Calendar c = Calendar.getInstance();
DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
c.setTime( sdf.parse("31/12/2010"));
out.println( c.get( Calendar.WEEK_OF_YEAR ) );
Prints 1
Same happens with Joda time.
:)
The definition of Week of Year is Locale dependent.
How it is defined in US is discused in the other posts. For example in Germany (DIN 1355-1 / ISO 8601): the first Week* of Year is the first week with 4 or more days in the new year.
*first day of week is Monday and last day of week is Sunday
And Java’s Calendar pays attention to the locale. For example:
public static void main(String[] args) throws ParseException {
DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date lastDec2010 = sdf.parse("31/12/2010");
Calendar calUs = Calendar.getInstance(Locale.US);
calUs.setTime(lastDec2010);
Calendar calDe = Calendar.getInstance(Locale.GERMAN);
calDe.setTime(lastDec2010);
System.out.println( "us: " + calUs.get( Calendar.WEEK_OF_YEAR ) );
System.out.println( "de: " + calDe.get( Calendar.WEEK_OF_YEAR ) );
}
prints:
us: 1
de: 52
ADDED
For the US (and I can think of that it is the same for Mexico) the 1. Week of Year is the week where the 1. January belongs to. -- So if 1. Januar is a Saturday, then the Friday before (31. Dec) belongs the same week, and in this case this day belongs to the 1. Week of Year 2011.
Values calculated for the WEEK_OF_YEAR
field range from 1 to 53. Week 1 for a
year is the earliest seven day period
starting on getFirstDayOfWeek() that
contains at least
getMinimalDaysInFirstWeek() days from
that year. It thus depends on the
values of getMinimalDaysInFirstWeek(),
getFirstDayOfWeek(), and the day of
the week of January 1. Weeks between
week 1 of one year and week 1 of the
following year are numbered
sequentially from 2 to 52 or 53 (as
needed).
To determine if that week is the last week of 2010 or the first of 2011 Java uses getMinimalDaysInFirstWeek javadoc. If that method returns 7 then the first week in which all the days in the week are of the same year is week one, if it returns 1 then the first week with any days of the next year is the first week of the next year.
In this case the first of January in 2011 is on a Saturday so it is considered the first week of 2011 as long as you would like a week with one day to be considered already the first week of the next year, if you don't then do:
Calendar c = Calendar.getInstance();
c.setMinimalDaysInFirstWeek(7);//anything more than 1 will work in this year
DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
c.setTime( sdf.parse("31/12/2010"));
System.out.println( c.get( Calendar.WEEK_OF_YEAR ) );
returns:
52
tl;dr
java.time.LocalDate.parse(
"31/12/2010" ,
DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( Locale.UK )
)
.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )
52
Or, add a library, and then…
org.threeten.extra.YearWeek.from( // Convert from a `LocalDate` object to a `YearWeek` object representing the entire week of that date’s week-based year.
java.time.LocalDate.parse( "31/12/2010" , DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( Locale.UK )
).getWeek() // Extract an integer number of that week of week-based-year, either 1-52 or 1-53 depending on the year.
52
java.time
As others noted, the definition of a week varies by Locale in the old java.util.Calendar class.
That troublesome class, and its partner java.util.Date, have been supplanted by the java.time framework built into Java 8 and later.
The IsoFields class defines a week using the ISO 8601 standard: Week always starts on a Monday, and week # 1 holds the first Thursday of the calendar-year.
Get the current moment.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
Ask about the standard week-based year.
int week = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR );
Standard week definition
There are many ways to define “a week” and “first week of the year”.
However, there is one major standard definition: the ISO 8601 standard. That standard defines weeks of the year, including the first week of the year.
the week with the year's first Thursday
A standard weeks begins with Monday and ends with Sunday.
Week # 1 of a week-based year has the first Thursday of the calendar year.
The java.time classes support the ISO 8601 week through the IsoFields class, holding three constants that implement TemporalField:
WEEK_OF_WEEK_BASED_YEAR
WEEK_BASED_YEAR
WEEK_BASED_YEARS
Call LocalDate::get to access the TemporalField.
LocalDate ld = LocalDate.parse( "2010-12-31" ) ;
int weekOfWeekBasedYear = ld.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int yearOfWeekBasedYear = ld.get( IsoFields.WEEK_BASED_YEAR ) ;
ld.toString(): 2010-12-31
weekOfWeekBasedYear: 52
yearOfWeekBasedYear: 2010
ISO 8601 string format
The ISO 8601 standard defines a textual format as well as a meaning for week-based-year values: yyyy-Www. For a specific date, add day-of-week numbered 1-7 for Monday-Sunday: yyyy-Www-d.
Construct such a string.
String outputWeek = String.format( "%04d" , yearOfWeekBasedYear ) + "-W" + String.format( "%02d" , weekOfWeekBasedYear ) ;
String outputDate = outputWeek + "-" + ld.getDayOfWeek().getValue() ;
2010-W52-5
YearWeek
This work is much easier if you add the ThreeTen-Extra library to your project. Then use the YearWeek class.
YearWeek yw = YearWeek.from( ld ) ; // Determine ISO 8601 week of a `LocalDate`.
Generate the standard string.
String output = yw.toString() ;
2010-W52
And parse.
YearWeek yearWeek = YearWeek.parse( "2010-W52" ) ;
yearWeek.toString(): 2010-W52
Determine a date. Pass a java.time.DayOfWeek enum object for day-of-week Monday-Sunday.
LocalDate localDate = yw.atDay( DayOfWeek.MONDAY ) ;
localDate.toString(): 2010-12-27
I strongly recommending adding this library to your project. Then you can pass around smart objects rather than dumb ints. Doing so makes your code more self-documenting, provides type-safety, and ensures valid values.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Using a JDBC driver compliant with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings nor java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
For much more detail, see my Answer on the similar Question:
java get week of year for given a date
…and see my Answer on the similar Question:
How to calculate Date from ISO8601 week number in Java
IIRC, The first week with a date of Jan 1 is week 1.
That's why week 1 is returned for 12/31/2010.
Try it for 12/31/2011 and you'll get 52.
Edit: Week is locale specific, sometimes defined as Sunday - Saturday, sometimes defined as Monday - Sunday
This is because the start of the week is local dependent.
In the US the Week 1 starts on the Sunday before Jan 1. In 2010 this is Dec 26. That's why Dec 31 is still week 1.
In Europe the week 1 starts on the Monday before Jan 1. In 2010 this is Dec 27. That's why also in Europe Dec 31 is still week 1.
java.time
As also specified in the accepted answer, the Week-of-Year is Locale dependent.
Demo:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.Locale;
class Main {
public static void main(String[] args) {
DateTimeFormatter parser = DateTimeFormatter.ofPattern("dd/MM/uuuu", Locale.ROOT);
LocalDate date = LocalDate.parse("31/12/2010", parser);
System.out.println(date.get(WeekFields.of(Locale.US).weekOfWeekBasedYear()));
System.out.println(date.get(WeekFields.of(Locale.GERMANY).weekOfWeekBasedYear()));
}
}
Output:
1
52
Learn more about the modern Date-Time API from Trail: Date Time.
Suppose I have a date, i.e. year, month and day, as integers. What's a good (correct), concise and fairly readable algorithm for computing the ISO 8601 week number of the week the given date falls into? I have come across some truly horrendous code that makes me think surely there must be a better way.
I'm looking to do this in Java but psuedocode for any kind of object-oriented language is fine.
tl;dr
LocalDate.of( 2015 , 12 , 30 )
.get (
IsoFields.WEEK_OF_WEEK_BASED_YEAR
)
53
…or…
org.threeten.extra.YearWeek.from (
LocalDate.of( 2015 , 12 , 30 )
)
2015-W53
java.time
Support for the ISO 8601 week is now built into Java 8 and later, in the java.time framework. Avoid the old and notoriously troublesome java.util.Date/.Calendar classes as they have been supplanted by java.time.
These new java.time classes include LocalDate for date-only value without time-of-day or time zone. Note that you must specify a time zone to determine ‘today’ as the date is not simultaneously the same around the world.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
Or specify the year, month, and day-of-month as suggested in the Question.
LocalDate localDate = LocalDate.of( year , month , dayOfMonth );
The IsoFields class provides info according to the ISO 8601 standard including the week-of-year for a week-based year.
int calendarYear = now.getYear();
int weekNumber = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR );
Near the beginning/ending of a year, the week-based-year may be ±1 different than the calendar-year. For example, notice the difference between the Gregorian and ISO 8601 calendars for the end of 2015: Weeks 52 & 1 become 52 & 53.
ThreeTen-Extra — YearWeek
The YearWeek class represents both the ISO 8601 week-based year number and the week number together as a single object. This class is found in the ThreeTen-Extra project. The project adds functionality to the java.time classes built into Java.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
YearWeek yw = YearWeek.now( zoneId ) ;
Generate a YearWeek from a date.
YearWeek yw = YearWeek.from (
LocalDate.of( 2015 , 12 , 30 )
)
This class can generate and parse strings in standard ISO 8601 format.
String output = yw.toString() ;
2015-W53
YearWeek yw = YearWeek.parse( "2015-W53" ) ;
You can extract the week number or the week-based-year number.
int weekNumber = yw.getWeek() ;
int weekBasedYearNumber = yw.getYear() ;
You can generate a particular date (LocalDate) by specifying a desired day-of-week to be found within that week. To specify the day-of-week, use the DayOfWeek enum built into Java 8 and later.
LocalDate ld = yw.atDay( DayOfWeek.WEDNESDAY ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I believe you can use the Calendar object (just set FirstDayOfWeek to Monday and MinimalDaysInFirstWeek to 4 to get it to comply with ISO 8601) and call get(Calendar.WEEK_OF_YEAR).
/* Build a calendar suitable to extract ISO8601 week numbers
* (see http://en.wikipedia.org/wiki/ISO_8601_week_number) */
Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);
/* Set date */
calendar.setTime(date);
/* Get ISO8601 week number */
calendar.get(Calendar.WEEK_OF_YEAR);
The joda-time library has an ISO8601 calendar, and provides this functionality:
http://joda-time.sourceforge.net/cal_iso.html
yyyy-Www-dTHH:MM:SS.SSS This format of
ISO8601 has the following fields:
* four digit weekyear, see rules below
* two digit week of year, from 01 to 53
* one digit day of week, from 1 to 7 where 1 is Monday and 7 is Sunday
* two digit hour, from 00 to 23
* two digit minute, from 00 to 59
* two digit second, from 00 to 59
* three decimal places for milliseconds if required
Weeks are always complete, and the
first week of a year is the one that
includes the first Thursday of the
year. This definition can mean that
the first week of a year starts in the
previous year, and the last week
finishes in the next year. The
weekyear field is defined to refer to
the year that owns the week, which may
differ from the actual year.
The upshot of all that is, that you create a DateTime object, and call the rather confusingly (but logically) named getWeekOfWeekyear(), where a weekyear is the particular week-based definition of a year used by ISO8601.
In general, joda-time is a fantastically useful API, I've stopped using java.util.Calendar and java.util.Date entirely, except for when I need to interface with an API that uses them.
Just the Java.util.Calendar can do the trick:
You can create a Calendar instance and set the First Day Of the Week
and the Minimal Days In First Week
Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(date);
// Now you are ready to take the week of year.
calendar.get(Calendar.WEEK_OF_YEAR);
This is provided by the javaDoc
The week determination is compatible with the ISO 8601 standard when
getFirstDayOfWeek() is MONDAY and getMinimalDaysInFirstWeek() is 4,
which values are used in locales where the standard is preferred.
These values can explicitly be set by calling setFirstDayOfWeek() and
setMinimalDaysInFirstWeek().
The Calendar class almost works, but the ISO week-based year does not coincide with what an "Olson's Timezone package" compliant system reports. This example from a Linux box shows how a week-based year value (2009) can differ from the actual year (2010):
$ TZ=UTC /usr/bin/date --date="2010-01-01 12:34:56" "+%a %b %d %T %Z %%Y=%Y,%%G=%G %%W=%W,%%V=%V %s"
Fri Jan 01 12:34:56 UTC %Y=2010,%G=2009 %W=00,%V=53 1262349296
But Java's Calendar class still reports 2010, although the week of the year is correct.
The Joda-Time classes mentioned by skaffman do handle this correctly:
import java.util.Calendar;
import java.util.TimeZone;
import org.joda.time.DateTime;
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(1262349296 * 1000L);
cal.setMinimalDaysInFirstWeek(4);
cal.setFirstDayOfWeek(Calendar.MONDAY);
System.out.println(cal.get(Calendar.WEEK_OF_YEAR)); // %V
System.out.println(cal.get(Calendar.YEAR)); // %G
DateTime dt = new DateTime(1262349296 * 1000L);
System.out.println(dt.getWeekOfWeekyear()); // %V
System.out.println(dt.getWeekyear()); // %G
Running that program shows:
53 2010 53 2009
So the ISO 8601 week number is correct from Calendar, but the week-based year is not.
The man page for strftime(3) reports:
%G The ISO 8601 week-based year (see NOTES) with century as a decimal number. The
4-digit year corresponding to the ISO week number (see %V). This has the same for‐
mat and value as %Y, except that if the ISO week number belongs to the previous or
next year, that year is used instead. (TZ)
If you want to be on the bleeding edge, you can take the latest drop of the JSR-310 codebase (Date Time API) which is led by Stephen Colebourne (of Joda Fame). Its a fluent interface and is effectively a bottom up re-design of Joda.
this is the reverse: gives you the date of the monday of the week (in perl)
use POSIX qw(mktime);
use Time::localtime;
sub monday_of_week {
my $year=shift;
my $week=shift;
my $p_date=shift;
my $seconds_1_jan=mktime(0,0,0,1,0,$year-1900,0,0,0);
my $t1=localtime($seconds_1_jan);
my $seconds_for_week;
if (#$t1[6] < 5) {
#first of january is a thursday (or below)
$seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)-#$t1[6]+1);
} else {
$seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)-#$t1[6]+8);
}
my $wt=localtime($seconds_for_week);
$$p_date=sprintf("%02d/%02d/%04d",#$wt[3],#$wt[4]+1,#$wt[5]+1900);
}