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.
Related
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.
I would like to get which day of the week is the current day and looking the SimpleDateFormat class I tought that the "F" is what I need. So I wrote a little test:
System.out.println(new SimpleDateFormat("F").format(new Date()));
Today is wednesday and I expect to get 3 as output. Instead I get 2.
As english isn't my mothertongue, did I missunderstand the meaning of the format?
F - Day of week in month
E - Day name in week
try u - Day number of week (1 = Monday, ..., 7 = Sunday)
Note that 'u' is since Java 7, but if you need just day number of the week then use Calendar
Calendar c = Calendar.getInstance();
System.out.println(c.get(Calendar.DAY_OF_WEEK));
You can change first day of week by changing Locale or directly as
c.setFirstDayOfWeek(Calendar.SUNDAY);
Today is the second Wednesday in the current month.
The java.util.Calendar/.Date and related classes are a confusing mess as you have learned the hard way. Counting from zero for month numbers and day-of-week numbers is one of many poor design choices made in those old classes.
java.time
Those old classes have been supplanted in Java 8 and later by the java.time framework. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
DayOfWeek
For day of week, use the well-named DayOfWeek enum.
If you want an integer for day-of-week compliant with ISO 8601 where Monday = 1 and Sunday = 7, you can extract that from an instance of DayOfWeek.
Conversion
If starting with a java.util.Calendar object, convert to java.time.
An Instant is a moment on the timeline in UTC.
Instant instant = myJavaUtilCalendarObject.toInstant();
Apply a time zone in order to get a date in order to get a day-of-week.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
DayOfWeek dayOfWeek = zdt.getDayOfWeek();
Note that time zone in crucial in determining a date, and therefore a day-of-week. The date is not the same around the world simultaneously. A new day dawns earlier in Paris than in Montréal, for example.
Number Of Day-Of-Week
Now that we have java.time and this enum built into Java, I suggest you freely pass around those enum instances rather than a magic number.
But if you insist on an integer, ask the DayOfWeek.
int dayOfWeekNumber = dayOfWeek.getValue();
String Of Day-Of-Week
That enum generates a localized String for the name of the day-of-week.
String output = dayOfWeek.getDisplayName( TextStyle.FULL , Locale.CANADA_FRENCH ); // Or Locale.ENGLISH.
Indexes for the days of week start from 0, not 1.
F -> Day of week in month(1-5)
Today is - 09/01/2013(dd/MM/yyyy) which fall 2nd in week so it has printed 2.
If you try with 16/01/2013 then it would print 3.
F = Day of Week in Month
1day to 7day, it will print 1.
8day to 14day, it will print 2.
15day to 21day, it will print 3.
22day to 28day, it will print 4 and
29day to 31day, it will print 5.
just use this method for this.
public String day(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("EEEEEEEEE", new Locale("tr", "TR"));
return sdf.format(date);
}
The code
String strDate = "2010-12-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
Date parsedDate = sdf.parse(strDate);
System.out.println(parsedDate);
will, dependend on your locale, produce the following output:
Fri Jan 01 00:12:00 CET 2010
The date is not parsed correctly, since i expect the 1st dec and not the 1st jan.
I know, months are numbered from 0 to 11, so the 12 becomes a 0 for january.
I have several solutions for this problem in mind, but all of them will produce at least 3-4 additional lines of code. So my question is:
What is the nicest way to solve this "problem"?
I can't imagine that it takes more than 2-3 lines to parse a simple date...
//edit: Shame on me for this question. Forgive me. thx folks
change yyyy-mm-dd to yyyy-MM-dd
M Month in year Month July; Jul; 07
m Minute in hour Number 30
See
SimpleDateFormat
Your date format is incorrect: Months are MM (not mm, which is for minutes). Try this:
"yyyy-MM-dd"
The reason you are getting January is that you haven't given a month to the parser (you gave year-minute-day). January, the first month, is the default month allocated to the date if not provided by the input. The 12 got parsed into the minute field (fairly obviously)
What is the nicest way to solve this "problem"?
Use different classes. You are using troublesome old legacy classes. Instead use the java.time classes.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone. It counts months sensibly, 1-12 is January through December.
Your input string is already in one of the ISO 8601 formats. These standard formats are used by default in the java.time classes. So no need with defining a formatting pattern.
LocalDate localDate = LocalDate.parse( "2010-12-01" );
Month
Also check out the handy Month enum.
Month month = Month.of( 1 ); // January = 1, December = 12.
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);
}