Why does SimpleDateFormat parse incorrect date? - java

I have date in string format and I want to parse that into util date.
var date ="03/11/2013"
I am parsing this as :
new SimpleDateFormat("MM/dd/yyyy").parse(date)
But the strange thing is that, if I am passing "03-08-201309 hjhkjhk" or "03-88-2013" or 43-88-201378", it does not throw error , it parses it.
For this now, I have to write regex pattern for checking whetehr input of date is correct or not.
but why is it so ??
Code :
scala> val date="03/88/201309 hjhkjhk"
date: java.lang.String = 03/88/201309 hjhkjhk
scala> new SimpleDateFormat("MM/dd/yyyy").parse(date)
res5: java.util.Date = Mon May 27 00:00:00 IST 201309

You should use DateFormat.setLenient(false):
SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
df.setLenient(false);
df.parse("03/88/2013"); // Throws an exception
I'm not sure that will catch everything you want - I seem to remember that even with setLenient(false) it's more lenient than you might expect - but it should catch invalid month numbers for example.
I don't think it will catch trailing text, e.g. "03/01/2013 sjsjsj". You could potentially use the overload of parse which accepts a ParsePosition, then check the current parse index after parsing has completed:
ParsePosition position = new ParsePosition(0);
Date date = dateFormat.parse(text, position);
if (position.getIndex() != text.length()) {
// Throw an exception or whatever else you want to do
}
You should also look at the Joda Time API which may well allow for a stricter interpretation - and is a generally cleaner date/time API anyway.

Jon Skeet’s answer is correct and was a good answer when it was written in 2013.
However, the classes you use in your question, SimpleDateFormat and Date, are now long outdated, so if someone got a similar issue with them today, IMHO the best answer would be to change to using the modern Java date & time API.
I am sorry I cannot write Scala code, so you will have to live with Java. I am using
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy");
The format pattern letters are the same as in your question, though the meaning is slightly different. DateTimeFormatter takes the number of pattern letters literally, as we shall see. Now we try:
System.out.println(LocalDate.parse(date, parseFormatter));
Results:
"03/11/2013" is parsed into 2013-03-11 as expected. I used the modern LocalDate class, a class that represents a date without time-of-day, exactly what we need here.
Passing "03/88/2013 hjhkjhk" gives a DateTimeParseException with the message Text '03/88/2013 hjhkjhk' could not be parsed, unparsed text found at index 10. Pretty precise, isn’t it? The modern API has methods to parse only part of a string if that is what we want, though.
"03/88/201309" gives Text '03/88/201309' could not be parsed at index 6. We asked for a 4 digit year and gave it 6 digits, which leads to the objection. Apparently it detects and reports this error before trying to interpret 88 as a day of month.
It does object to a day of month of 88 too, though: "03/88/2013" gives Text '03/88/2013' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 88. Again, please enjoy how informative the message is.
"03-08-2013" (with hyphens instead of slashes) gives Text '03-08-2013' could not be parsed at index 2, not very surprising. Index 2 is where the first hyphen is.
Jon Skeet explained that the outdated SimpleDateFormat can be lenient or non-lenient. This is true for DateTimeFormatter too, in fact it has 3 instead of 2 resolver styles, called ‘lenient’, ‘smart’ and ‘strict’. Since many programmers are not aware of this, though, I think they made a good choice of not making ‘lenient’ the default (‘smart’ is).
What if we wanted to make our formatter lenient?
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy")
.withResolverStyle(ResolverStyle.LENIENT);
Now it also parses "03/88/2013", into 2013-05-27. I believe this is what the old class would also have done: counting 88 days from the beginning of March gives May 27. The other error messages are still the same. In other words it still objects to unparsed text, to a 6 digit year and to hyphens.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (that’s ThreeTen for JSR-310, where the modern API was first defined).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.

Related

Unable to convert string to LocalDateTime object

I am trying to convert a string to LocaleDateTime object in Java8 as below :
DateTimeFormatter globalFormat = DateTimeFormatter.ofPattern("yyyyMMddhhmmssSS");
String input = "2019082905020425";
LocalDateTime currentDateTime = LocalDateTime.parse(input, globalFormat);
But I am getting below exception, if someone can help me with a solution on the same :
Exception in thread "main" java.time.format.DateTimeParseException:
Text '2019082905020425' could not be parsed at index 0 at
java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1947)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1849)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)at test.main(Test.java:20)
It’s a bug in Java 8.
Workaround for Java 8
DateTimeFormatter globalFormat = new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
String input = "2019082905020425";
String adaptedInput = input + "0";
LocalDateTime currentDateTime = LocalDateTime.parse(adaptedInput, globalFormat);
System.out.println("Parsed date and time: " + currentDateTime);
Output from this snippet is (tested on jdk-1.8.0_121):
Parsed date and time: 2019-08-29T05:02:04.250
Java 8 cannot separate an integer field like ss and a fractional fields like SS without any separator between them. The workaround is to parse the fraction as an integer too. Your string includes 100ths of seconds, and no integer field for those is built in. So I append an extra 0 (zero) so that we’ve got milliseconds, and then use ChronoField.MILLI_OF_SECOND for parsing.
Whether it was really a bug can maybe be debated. There never was any strict promise in the docs that it should work, but it seemed to be the expectation of many, and in any case they fixed it in Java 9.
I have made one more correction, and you will want to check whether this is the correction you want: Lowercase hh is for hour within AM or PM from 01 through 12. If you intended this, you need to specify whether you want AM or PM. Instead I assumed that 05 was an hour of day from 00 through 23. Use uppercase HH for parsing this.
Edit: use a regular expression? #josejuan advocates a regular expression over the above. It’s an option, and can save us of the explicit formatter completely:
String input = "2019082905020425";
String adaptedInput = input.replaceFirst(
"^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})$",
"$1-$2-$3T$4:$5:$6.$7");
LocalDateTime currentDateTime = LocalDateTime.parse(adaptedInput);
The result is the same as before. For my part I find the latter code quite a lot harder to read and maintain. Also once you migrate to Java 9 or higher, I think that the first snippet above lends itself more directly to going back to the code from which you started, which is what you want in the end. Pick the solution that you prefer.
The code is working on Java 9 and later
On Java 9 and later the change from hh to HH is all we need for the code in the question to work fine.
Links
Java bug DateTimeFormatter won't parse dates with custom format "yyyyMMddHHmmssSSS" in the bug database
Question Is java.time failing to parse fraction-of-second? about the bug
Question Comparing two times in android about hh in a format pattern string

How to convert date from English to Arabic

I have this code
frame.sigdate.setText(new SimpleDateFormat("yyyy/M/d").format(new Date()));
which reads the date from my PC with English numbers. What I want to do is convert the date to Arabic numbers.
Is there anything like Local.ar ?
I appreciate any help.
java.time
Locale arabicLocale = Locale.forLanguageTag("ar");
DateTimeFormatter arabicDateFormatter
= DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(arabicLocale)
.withDecimalStyle(DecimalStyle.of(arabicLocale));
LocalDate today = LocalDate.now(ZoneId.of("Asia/Muscat"));
System.out.println(today.format(arabicDateFormatter));
Output:
١٥‏/٤‏/٢٠١٨
The key is withDecimalStyle. Without this call, the formatter would still use western numerals, as in 15‏/4‏/2018. You may want to use a more specific language tag than just ar for Arabic, for example ar-BH for Bahrain or ar-YE for Yemen. See the link at the bottom for possibilities. You should also insert your desired time zone where I put Asia/Muscat.
EDIT: The above has been tested in Java 9. Surprisingly in Java 8 it still uses western (unlocalized) digits. A possible fix (or workaround if you like) is to specify the zero digit explicitly — it will pick up the other digits from it.
DecimalStyle arabicDecimalStyle
= DecimalStyle.of(arabicLocale).withZeroDigit('٠');
DateTimeFormatter arabicDateFormatter
= DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(arabicLocale)
.withDecimalStyle(arabicDecimalStyle);
It’s an Arabic zero between the two apostrophes in the argument to withZeroDigit. Now I get this output on Java 8:
١٥/٠٤/١٨
It’s usually a good idea to use the built-in locale specific formats as I do with ofLocalizedDate in both snippets above. If you need finer control over the format, use ofPattern instead. For example, to get yyyy/mm/dd format:
DateTimeFormatter arabicDateFormatter
= DateTimeFormatter.ofPattern("uuuu/MM/dd", arabicLocale)
.withDecimalStyle(arabicDecimalStyle);
Output:
٢٠١٨/٠٤/١٥
The reason why the format changed from Java 8 to Java 9 is that Java has changed the defaults for where the locale data come from, including the built-in localized date and time formats. You can get the Java 9 format already in Java 8 by setting a system property, for example like this:
System.setProperty("java.locale.providers", "CLDR,JRE,SPI");
With this change the first code snippet above gives the same output on Java 8 as on Java 9:
١٥‏/٤‏/٢٠١٨
The important detail here is that CLDR goes first in the property string. And the advantages are you don’t need to specify your own format pattern string, localization to other locales is straightforward and users won’t be surprised by a change in behaviour once you switch to Java 9 or later.
I am using and recommending java.time, the modern Java date and time API. The SimpleDateFormat class that you used in the question is not only long outdated, it is also notoriously troublesome. IMHO you should avoid it completely. The modern API is so much nicer to work with.
Links
Oracle tutorial: Date Time explaining how to use java.time.
List of supported locales in Java 8
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/MM/yyyy");
String date = "16/08/2011";
Locale arabicLocale = Locale.forLanguageTag("ar-SA");
DateTimeFormatter arabicDateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(arabicLocale).withDecimalStyle(DecimalStyle.of(arabicLocale));
LocalDate today = LocalDate.now(ZoneId.of("Asia/Muscat"));
today = LocalDate.parse(date, formatter);
String dat = today.format(arabicDateFormatter);
System.out.println(dat);
out put ١٦‏/٨‏/٢٠١١
try below approch
java.util.Locale locale = new java.util.Locale("ar");
java.text.DecimalFormat df = (java.text.DecimalFormat)
java.text.DecimalFormat.getNumberInstance(locale);
DateTime dateTimeObjectInUTC = new DateTime(DateTimeZone.UTC);
DateTimeZone dateTimeZoneObject = DateTimeZone.forID("Asia/Riyadh");
java.util.Locale locale = new Locale("ar","SA");
DateTimeFormatter formatter = DateTimeFormat.forStyle("FF").withLocale(locale).withZone(dateTimeZoneObject);
String output = formatter.print(dateTimeObjectInUTC);
This should help!
I am using Joda-Time. Please refer to the Jodatime documentation. DateTimeZone documentation, for example.

ParseException when trying to parse Date string

I have a piece of code written to parse date string -
DateFormat cal = new SimpleDateFormat("yyyy-MM-dd hh:mm");
cal.setLenient(false);
try {
cal.parse("2018-01-01 14:42");
}
catch (Exception e)
{
e.printStackTrace();
}
}
But I get an exception saying -
java.text.ParseException: Unparseable date: "2018-01-01 14:42"
at java.base/java.text.DateFormat.parse(DateFormat.java:388)
at MyClass.main(MyClass.java:10)
I am not sure why I am seeing this error as the date string and the format given is right. Please help
From the documentation https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html:
Lowercase h in the SimpleDateFormat indicates hour in the 12 hour format, whereas 24-hour format is indicated with uppercase H. As 14 > 12, the date 14:42 fails to be parsed.
You should be using HH instead of hh for hour pattern if the hour is displayed in 24 hour format.
See the documentation below for more information.
https://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html
hh is used for the time of the day with hours going from 1 to 12. 14 is not a valid hour for this kind of hour-representation, so you have to use HH or kk. The former is used for times that are shown from 0-23, the latter for times shown between 1-24. Most likely you have to use HH or H/k if the single digit hours aren't preceded by a 0.
TL;DR
DateTimeFormatter cal = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm");
LocalDateTime.parse("2018-01-01 14:42", cal);
This runs without exception or other error.
java.time
The date-time classes you use, DateFormat and SimpleDateFormat, are long outdated and furthermore notoriously troublesome. I recommend you stop using them immediately. Instead use java.time, the modern Java date and time API. It came out nearly four years ago after having been described in Java Specification Request (JSR) 310 (a name that somehow still clings to the API).
As others have correctly pointed out, your error was that you used lowercase hh in your format pattern string, where you should have used uppercase HH for hour of day. Just one little example of where the modern classes try to be more helpful, try the same. If I insert hh in the format pattern in the code above, my program crashes (because there is no try-catch construct) with an DateTimeParseException with the following message:
Text '2018-01-01 14:42' could not be parsed: Invalid value for
ClockHourOfAmPm (valid values 1 - 12): 14
While perhaps still a bit esoteric, it is very precise. And I would dare hope that in combination with the documentation it would tell you what you did wrong.
The exception is unchecked, so no try-catch is required around the parsing. On the other hand, you may use one if you like (and if you are not very certain that the format of your date-time string is correct, you should).
Links
Oracle tutorial: Date Time, explaining how to use java.time.
Java Specification Request (JSR) 310, where the modern date and time API was first described.

DateFormat.parse() giving wrong result [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 years ago.
Improve this question
I have played with this for almost 6 hours now and looked at other questions but can't seem to figure out what I'm doing wrong that is giving me these odd results. Here is my code below
String dateStr = "20171230";
StringBuilder sb = new StringBuilder();
sb.append(dateStr.subSequence(0, 4))
.append("/")
.append(dateStr.substring(4,6))
.append("/")
.append(dateStr.substring(6,8));
dateStr = sb.toString();
System.out.println("date string is " + dateStr);
DateFormat df = new SimpleDateFormat("yyyy/mm/dd");
df.setLenient(false);
Date Date = null;
try {
Date = df.parse(dateStr);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Date is " + Date);
output is
date string is 2017/12/30
Date is Mon Jan 30 00:12:00 CST 2017
The date should be December 30, not January 30. Can someone tell my why this is happening.
The format you passed one simple date format has minutes defined where you think the month is.
Notice how the time output is set too 00:12.
Instead, use
yyyy/MM/dd
Allow me to give a couple of suggestions that go a bit beyond exactly what you asked about. I think my suggestions should be helpful.
First, you shouldn’t want to stick to the long outdated classes Date and SimpleDateFormat. The modern Java date and time API is generally much nicer to work with and contains fewer unpleasant surprises. It is known as JSR-310 or java.time.
Second, there should be no need to reformat your string using a StringBuilder before extracting the date from it. 20171230 can be parsed into a date directly. Even worse, the way you are converting the string, you are not validating the length: if there happened to be 9 digits instead of 8, you would want to catch it and throw an exception, but your code just takes the first 8 digits and ignores if there are more.
Finally, as Trenton Trama already pointed out in the other answer, format pattern strings are case sensitive. It matters whether and where you use uppercase or lowercase letters. Using the incorrect case will most likely lead to incorrect results. This goes for both the outdated and the modern API. Fortunately, when it comes to parsing, the modern API is much more inclined to throw an exception when you use the wrong case so at least you are informed something is wrong.
And finally-finally, when you want someone else to read your code, like other stackoverflowers, use the coding conventions to help us. Variable names begin with a lowercase letter. It’s particularly confusing when you have a class named Date and a variable also named Date.
The above sums up to:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMdd");
String dateStr = "20171230";
LocalDate date = LocalDate.parse(dateStr, dtf);
System.out.println("Date is " + date);
Output is:
Date is 2017-12-30
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (that’s ThreeTen for JSR 310, where the modern API was first defined).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.

Incorrect Parsed Date

My code:
DateFormat format = new SimpleDateFormat("dd/MM/YYYY");
Date Today = format.parse(today);
Date Date = format.parse(date);
difference = (Date.getTime() - Today.getTime()) / 86400000;
Math.abs(difference);
System.out.println(String.valueOf("date" + date));
System.out.println(String.valueOf("date" + Date));
System.out.println(String.valueOf("date" + today));
System.out.println(String.valueOf("date" + Today));
The output:
date29/11/2016
dateSun Dec 27 00:00:00 GMT+08:00 2015
date20/11/2016
dateSun Dec 27 00:00:00 GMT+08:00 2015
I have problems while parsing the date, the original date is 29/11/2016, however when parsed, the date becomes Sun Dec 27 00:00:00 GMT+08:00 2015.
This problem appears in my whole program whenevr it's related to date.
Format dd/MM/YYYY should be dd/MM/yyyy. See javadoc of SimpleDateFormat: y = Year, Y = Week year. – Andreas 12 mins ago
Thanks resolved.
I know you got your solution from Andreas’ comment already. What I want to do here is take a step back and suggest a few improvements to your code. Feel free to ignore.
Most importantly I suggest you throw the long outdated classes Date, DateFormat and SimpleDateFormat overboard and start using their modern replacements. These came out in the java.time package early in 2014. These also offer a much more straightforward and clear way of calculating the difference.
Follow the convention that says that a variable name begins with a lowercase letter. Specifically, in the same source file to use a class called Date and two variable called date and Date is bound to cause confusion.
Like Henry I also think you intended difference = Math.abs(difference);.
Your calls to String.valueOf() are superfluous and just seem to make the code a bit harder to read. Drop them.
For the sake of the example, in the code I suggest below, I am deliberately using your incorrect date format pattern string, dd/MM/YYYY.
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd/MM/YYYY");
LocalDate todayAsLocalDate = LocalDate.parse(today, format);
LocalDate dateAsLocalDate = LocalDate.parse(date, format);
difference = ChronoUnit.DAYS.between(todayAsLocalDate, dateAsLocalDate);
difference = Math.abs(difference);
System.out.println("date " + date);
System.out.println("date " + dateAsLocalDate);
System.out.println("date " + today);
System.out.println("date " + todayAsLocalDate);
System.out.println(difference);
As the code stands now, it throws a java.time.format.DateTimeParseException: Text '20/11/2016' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {WeekBasedYear[WeekFields[SUNDAY,1]]=2016, MonthOfYear=11, DayOfMonth=20},ISO of type java.time.format.Parsed. When the code is incorrect, I much prefer an exception over an incorrect result that might go unnoticed. So this is better than what SimpleDateFormat gave you.
While the message is not easy to read, the bit to notice is WeekBasedYear. Week-based years are only useful with week numbers, you intended none of that. If you compare with the documentation, you will see that uppercase Y in the pattern is week-based-year while lowercase y is year-of-era. So let’s correct:
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd/MM/yyyy");
Now the code prints:
date 29/11/2016
date 2016-11-29
date 20/11/2016
date 2016-11-20
9
We note one final advantage of the modern classes: you can have a date without a time-of-day when this is what you need, again giving code that models your requirements more precisely and thereby leaves less room for confusion.
Question: can I use the modern classes with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen-Backport library, the backport of the new classes (that’s “ThreeTen” for JSR 310, where the modern API was first defined).
On Android, use the Android edition of ThreeTen-Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.

Categories