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.
This question already has answers here:
ParseException when parsing 3 character abbreviated month using SimpleDateFormat
(5 answers)
Closed 4 years ago.
i have a SimpleDateFormat format = new SimpleDateFormat("d M y H:m"); and i try to parse the String "8 Jan 2019 16:47" with it, but i get a ParseException. Did i create it the wrong way?
According to docs.oracle.com the M should recognize 3-letter-months.
Can anyone help me?
The official documentation: (https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html)
You probably missed out this little note here:
Month: If the number of pattern letters is 3 or more, the month is interpreted as text; otherwise, it is interpreted as a number.
Based on your example input, the following works:
SimpleDateFormat format = new SimpleDateFormat("dd MMM yyyy HH:mm");
java.time
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMM y H:mm", Locale.ENGLISH);
String stringToParse = "8 Jan 2019 16:47";
LocalDateTime dateTime = LocalDateTime.parse(stringToParse, formatter);
System.out.println(dateTime);
The output from this snippet is:
2019-01-08T16:47
What went wrong in your code?
SimpleDateFormat and Date are poorly designed and long outdated, the former in particular notoriously troublesome. I recommend you don’t use them in 2019.
As others have said you need three M for month abbreviation (no matter if you are using the outdated SimpleDateFormat or the modern DateTimeFormatter). One M will match a month number in 1 or 2 digits, for example 1 for January.
You should also specify a locale for your formatter. I took Jan to be English so specified Locale.ENGLISH. If you don’t specify locale, the JVM’s default locale will be used, which may work well on some JVMs and suddenly break some day when the default locale has been changed or you are trying to run your program on a different computer.
Link
Oracle tutorial: Date Time explaining how to use java.time.
This question already has answers here:
last day of month calculation
(10 answers)
Closed 6 years ago.
I have a given date:
01/10/2017(mm/dd/yyyy)
Calendar c = c.getInstance();
c.setTime(date);
Now to point to the last day of the month I am using the following code:
c.set(Calendar.Date, c.getActualMaximum(Calendar.Date));
Expected Output : 01/31/2017
Original Output : 02/01/2017
I am not getting the expected output. Its returning me the first day of next month.
Can anyone please help me?
You better use the new date time features of Java 8 here:
LocalDate date = LocalDate.of(2000, Month.OCTOBER, 15);
LocalDate lastOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
System.out.printf("last day of Month: %s%n", lastOfMonth );
Yes, you could theoretically also use Calendar objects and do all kinds of low level operations yourself. But chances are: you will get that wrong ... well, unless you look here and follow the advise from there.
But as Basil is correctly pointing out: The 310 project is pretty close to java.time (because the later was designed to match the former); and 310 can be "ported" back to Java7. So if there is more than one place where you need to deal with dates, I would look into exactly that: making your life easier by using the "sane" date/time library.
I have doubts about mm/dd/yyyy. Try this
Date d = new SimpleDateFormat("MM/dd/yyyy").parse("01/10/2017");
Calendar c = Calendar.getInstance();
c.setTime(d);
c.set(Calendar.DATE, c.getActualMaximum(Calendar.DATE));
System.out.println(c.getTime());
it gives expected date:
Tue Jan 31 00:00:00 EET 2017
This question already has answers here:
java date problem in parsing
(4 answers)
Closed 7 years ago.
I am getting a date by ajax in String format. But it is getting changed when I am converting it to date by SimpleDateFormat. The month is always changed to Jan. I am worried only about the month change.My code is given below
String appointmentDate = request.getParameter("appointmentDate");
System.out.println(" appointment date in String format "+appointmentDate);
Here I am getting the date correctly(16/12/2015). But when I am changing it to Date format it is getting changed(Fri Jan 16 00:12:00 IST 2015). Whatever I input the month, say August, May, June, I am always getting month Jan.
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
Date parsedDate = dateFormat.parse(appointmentDate);
System.out.println(" appointment date in DATE format "+parsedDate);
Please help me out. Thanks in advance.
As per the JavaDoc, lower case m denotes minutes, not months.
Changing your expression to dd/MM/yyyy should fix the issue.
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.