java.time.format.DateTimeParseException when parsing year - java

jdk used : 1.8
Not sure what is the issue, configuredFormat is valid one, inputTime is also valid one, really confused what is the issue.
public class Test {
public static void main(String[] args) {
String configuredFormat = "yyyyMMddHHmmssSSS";
String inputTime = "20200203164553123";
DateTimeFormatter dt = DateTimeFormatter.ofPattern(configuredFormat);
DateTimeFormatter strictTimeFormatter = dt.withResolverStyle(ResolverStyle.STRICT);
try {
LocalTime.parse(inputTime, strictTimeFormatter);
System.out.println("success");
} catch (DateTimeParseException | NullPointerException e) {
e.printStackTrace();
}
}
}
Exception I am Getting :
java.time.format.DateTimeParseException: Text '20200203164553123' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalTime.parse(LocalTime.java:441)
at com.Test.main(Test.java:20)

Lucky for you, there is an exact bug report which uses the exact same pattern that you're trying. Who better to explain than the JDK maintainers?
JDK-8031085
Workaround
DateTimeFormatter dtf = new
DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND,3)
.toFormatter()
Adjacent value parsing is generally a hard problem. It is intended to
handle the case where the first element is variable width (the year)
and all other elements are fixed width (month, day etc). However, the
"S" pattern letter is a fraction, not a value. Specifically, the
fraction can be variable width - more or less than three digits are
possible options. Given the general case of a variable width year and
a variable width millisecond, it is not possible to determine which of
the two fields was intended to be variable.
Having said that, the implementation (and javadoc) have not ended up
as I intended. The description of "fraction" in DateTimeFormatter
describes actions in strict and lenient mode, but there is no way to
access strict or lenient mode when using
DateTimeFormatter.ofPattern(). This is a documentation bug that should
be fixed by removing the discussion of strict vs lenient.
Worse however is that the SSS pattern has therefore ended up using
strict mode when lenient mode would be appropriate. As it currently
stands, DateTimeFormatter.ofPattern("hhmmss.SSS") requires three
digits for milliseconds, when it was originally intended to require 0
to 9 (the lenient behaviour).
I tried changing the whole of the DateTimeFormatter.ofPattern() method
to use lenient parsing, and it broke no tests (which is bad in its own
way). This might be a valid fix, but only if included in JDK 8, as
once people adapt to the strict parsing it will be hard to make it
lenient.
Given that the current implementation requires three digits for SSS,
it is thus very surprising that adjacent value parsing does not apply.

Actually I agree format should be valid... this seems to be confirmed as I tried with both oracle java 8 and 9 runtime, and with java 9 it does not happen. (I tried IBM jre 8 too and it works as well)
System.out.println( System.getProperty( "java.vendor" )+" - "+System.getProperty( "java.version" ) );
String configuredFormat = "yyyyMMddHHmmssSSS";
String inputTime = "20200203164553123";
DateTimeFormatter dt = DateTimeFormatter.ofPattern(configuredFormat);
DateTimeFormatter strictTimeFormatter = dt.withResolverStyle(ResolverStyle.STRICT);
try {
//System.out.println( dt.parse( inputTime ) );
LocalTime.parse(inputTime, strictTimeFormatter);
System.out.println("success");
} catch (DateTimeParseException | NullPointerException e) {
e.printStackTrace();
}
Output
Oracle Corporation - 9.0.4
success
IBM Corporation - 1.8.0_211
success
Oracle Corporation - 1.8.0_172
java.time.format.DateTimeParseException: Text '20200203164553123' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(Unknown Source)
at java.time.format.DateTimeFormatter.parse(Unknown Source)
at java.time.LocalTime.parse(Unknown Source)
at test.Test2.main(Test2.java:19)

Related

Bug in Finnish date localization?

I'm trying to localize for Finland using this code:
Locale finLocale = new Locale("fi", "FI");
Date today = new Date(2017, 1, 1);
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG, finLocale);
String formattedDate = dateFormat.format(today);
System.out.println(formattedDate);
What I end up getting is "helmikuutata". I would expect "helmikuu" or "helmikuuta", but this just seems wrong.
Is this valid Finnish, or is there a bug in Java? My version is 1.8.0_31
Yes, this was a bug in JDK (See JDK-8074791), wherein an extra 'ta' was appended to the month name. This got fixed from JDK 8u60 version onwards. So, if you upgrade to latest JDK versions like JDK8u131, you will get the correct output.
I am convinced that the answer by Pallavi Sonal is correct. I have already upvoted it and you should probably accept it. I had wanted to keep the following a comment, but it deserves better formatting, so here goes.
java.time
Since you are using Java 8 (and even if you didn’t), you will prefer the modern more programmer friendly API of java.time:
LocalDate today = LocalDate.of(2017, Month.FEBRUARY, 1);
DateTimeFormatter dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
.withLocale(finLocale);
String formattedDate = today.format(dateFormat);
On my Java 1.8.0_131 it gives the expected
1. helmikuuta 2017
If someone reading this is using Java 6 or 7, please consider getting the ThreeTen Backport library so you can use the modern date and time API as shown.

DateTimeFormatter pattern with liternal and no separator does not work

The parser generated by DateTimeFormatter.ofPattern exhibits the following interesting behaviour which is preventing me from writing a pattern to parse a string like 20150100:
System.out.println(DateTimeFormatter.ofPattern("yyyyMM").parse("201501", YearMonth::from)); // works
System.out.println(DateTimeFormatter.ofPattern("yyyyMM'aa'").parse("201501aa", YearMonth::from)); // works
System.out.println(DateTimeFormatter.ofPattern("yyyyMM'00'").parse("20150100", YearMonth::from));
// java.time.format.DateTimeParseException: Text '20150100' could not be parsed at index 0
I debuged the code, it seems the problem is caused by the year field parsing beyond the end of the string (max width for three y's and more is always 19). However, I don't understand how it could work for the pattern without the '00' literal at the end.
Is there any way to fix this withing having to use a formatter builder?
Edit:
Since Jarrod below confirmed it's buggy, I did more googling and finally found the bug reports:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8031085
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8032491
Both are only fixed in Java 9 though......
There is a bug in the DateTimePrinterParser:
I step debugged all the way through it, apparently you can not have digits as literals. Similar test codes proves this if you step debug all the way through to the DateTimeFormatterBuilder.parse() method you can see what it is doing wrong.
Apparently the Value(YearOfEra,4,19,EXCEEDS_PAD) parser consumes the 00 where they stop if those are not digits because it is looking for a number 4 to 19 digits long. The DateTimeFormatter that is embedded in the DateTimeParseContext is wrong.
If you put a non-digit character literal like xx it works, digit literals don't.
Both of these fail:
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'00'");
System.out.println(sdf.parse("20150100"));
Exception in thread "main" java.text.ParseException: Unparseable date:
"20150100" at java.text.DateFormat.parse(DateFormat.java:366)
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'00'");
System.out.println(dateTimeFormatter.parse("20150100", YearMonth::from));
Exception in thread "main" java.time.format.DateTimeParseException:
Text '20150100' could not be parsed at index 0 at
java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at
java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
Both of these succeed:
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'xx'");
System.out.println(sdf.parse("201501xx"));
Thu Jan 01 00:00:00 EST 2015
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'xx'");
System.out.println(dateTimeFormatter.parse("201501xx", YearMonth::from));
2015-01
If you don't mind to use a 3rd-party-library then you might try my library Time4J whose newest version v4.18 can do what you wish:
import net.time4j.Month;
import net.time4j.range.CalendarMonth;
import net.time4j.format.expert.ChronoFormatter;
import net.time4j.format.expert.PatternType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.text.ParseException;
import java.util.Locale;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
#RunWith(JUnit4.class)
public class CalendarMonthTest {
#Test
public void parse2() throws ParseException {
assertThat(
ChronoFormatter.ofPattern(
"yyyyMM'00'",
PatternType.CLDR,
Locale.ROOT,
CalendarMonth.chronology()
).parse("20150100"),
is(CalendarMonth.of(2015, Month.JANUARY)));
}
}
By the way, the links to the JDK-bug-log are not really related to your problem. Those issues only describe problems when applying adjacent digit parsing in context of fractional seconds. While that problem will be fixed with Java-9, your problem will not. Maybe you wish to open a new issue there? But I doubt that Oracle will treat it as bug. It is rather a new feature not supported until now by any library distributed by Oracle. Literals with (leading) digits are not expected in JSR-310 (aka java.time-package) to take part into adjacent-value-parsing (and in SimpleDateFormat also not).
Side note: Time4J is not just an answer to this detail (digit literals) but generally offers better performance in parsing and can be used in parallel with JSR-310 due to a lot of conversion methods. For example: To achieve an instance of YearMonth, just call calendarMonth.toTemporalAccessor() on the parsed result.
As an addendum to user177800's answer, you can use this form instead:
var formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral("00")
.toFormatter();
YearMonth.parse("20220200", formatter);
All part of java.time.

DateFormatSymbol's behavior is different between Android and JRE 1.7

I've made a jar for parsing a dateformat from text for JRE and Android.
Mostly It worked well. but when I try to parsing the following Chinese chars, It fails on Android and works on JRE. '五月' is May in Chinese.
"06 五月 2014"
I used the following code to parse it
String input = "06 五月 2014"
SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy", Locale.CHINESE);
Date date = df.parse(input);
So, i started narrowing down the problems and got the following test cases.
on Android,
DateFormatSymbols dfs = new DateFormatSymbols(Locale.CHINA);
String[] months = dfs.getMonths();// months[0] = 1月, months[1] = 2月 ...
String[] ampm = dfs.getAmPmStrings(); // ampm[0] = AM ampm[1] = PM
on JRE 1.7,
DateFormatSymbols dfs = new DateFormatSymbols(Locale.CHINA);
String[] months = dfs.getMonths();// months[0] = 一月, months[1] = 二月 ...
String[] ampm = dfs.getAmPmStrings(); // ampm[0] = 上午 ampm[1] = 下午
Why this happens? is this normal operation or am i missing something ?
Ignoring the difference between Java and Android, your code is not even guaranteed to work on different Java VMs. The API documentation does not cover a formal specification of the different date formatters and you are not guaranteed that the formatted output from one Java VM can be parsed by another Java VM.
In this case, the localization database in the Oracle VM uses "五月" as the Chinese word for the month of May (literary: "fifth moon"). Your Android localization database uses "5月" (literary: "5th moon"), which is just as correct, but different.

DecimalFormat not working on Windows 7, but working on Windows 8

I have an applet which is working fine on Windows 8, but on Windows 7 I get the following error:
Exception in thread "Thread-13" java.lang.NumberFormatException: For input string: "-0,9"
at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
at java.lang.Float.parseFloat(Unknown Source)
at Tanc.Game$Corp.getDW(Game.java:1505)
at Tanc.Game.borders(Game.java:975)
at Tanc.Game.loose(Game.java:1068)
at Tanc.Game.gameLoop(Game.java:242)
at Tanc.Game$1.run(Game.java:144)
I have to mention that I tried on 2 different computers but the same problem. On Windows 8 I don't get this error...
And code
String zz = new DecimalFormat("#.##").format(corp400.y);
System.out.println(zz);
if (Float.parseFloat(zz) == 0.2f)
sw = true;
if (Float.parseFloat(zz) == -0.24f)
sw = false;code here
Probably problem with locale: on the computer with Win8 there are locale, where , means decimal separator, but on the other tested computers . means decimal separator.
I replaced DecimalFormat with (Math.round(corp400.y*100.0)/100.0)
But if anyone can find out how to fix DecimalFormat to work with Windows 8 and windows 7 please Comment/Answer.
Probably on windows 8 (where it works) you are using an English locale.
The problem, tough, is in the code:
String zz = new DecimalFormat("#.##").format(corp400.y);
actually uses the default locale to format corp400.y, but the JavaDoc says that Float.parseFloat
Returns a new float initialized to the value represented by the specified String, as performed by the valueOf method of class Float.
This means that, since Float.valueOf parses a floating point literal, it expects decimals separated by . not ,
The correct way to reverse your locale-dependant formatting is:
float value = new DecimalFormat("#.##").parse(zz).floatValue();
Alternatively, you could use the locale-independent formatting on both ways as in:
String zz = String.valueOf(corp400.y);
float value = Float.parseFloat(zz)

What is the difference between {0} and {0,number,integer}

I'm trying to understand more about Java's MessageFormat utilities, and in examples in our codebase and elsewhere I see both {0} and {0,number,integer} being used for numbers, but I'm not sure which, if either, is preferable.
A quick test printing the differences:
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.Locale;
public class MessageFormatTest
{
public static void main(String[] args){
MessageFormat simpleChoiceTest = new MessageFormat("{0}");
MessageFormat explicitChoiceTest = new MessageFormat("{0,number,integer}");
int[] set = new int[]{0,1,4,5,6,10,10000,24345};
Locale[] locs = new Locale[]{Locale.US,Locale.UK,Locale.FRANCE,Locale.GERMANY};
for(Locale loc : locs){
simpleChoiceTest.setLocale(loc);
explicitChoiceTest.setLocale(loc);
for(int i : set){
String simple = simpleChoiceTest.format(new Object[]{i});
String explicit = explicitChoiceTest.format(new Object[]{i});
if(!simple.equals(explicit)){
System.out.println(loc+" - "+i+":\t"+simple+
"\t"+NumberFormat.getInstance(loc).format(i));
System.out.println(loc+" - "+i+":\t"+explicit+
"\t"+NumberFormat.getIntegerInstance(loc).format(i));
}
}
}
}
}
Outputs:
fr_FR - 10000: 10 000 10 000
fr_FR - 10000: 10,000 10 000
fr_FR - 24345: 24 345 24 345
fr_FR - 24345: 24,345 24 345
de_DE - 10000: 10.000 10.000
de_DE - 10000: 10,000 10.000
de_DE - 24345: 24.345 24.345
de_DE - 24345: 24,345 24.345
Which surprised me, if anything I would have expected the {0} to not do anything to the number, and for {0,number,integer} to localize it properly. Instead, both get localized, but it seems the explicit form always uses en_US localization.
According to the linked documentation, {0} gets put through NumberFormat.getInstance(getLocale()) while while the explicit form uses NumberFormat.getIntegerInstance(getLocale()). Yet when I call those directly (the last column in the output) both seem identical, and both localize correctly.
What am I missing here?
You are right. When you use "MessageFormat("{0,number,integer}")", formatter uses default locale(en_US) at the time of initialization and numbers are marked to use Integer format in default locale(en_US) as the code below is executed during the initialization time itself.
// this method is internally called at the time of initialization
MessageFormat.makeFormat()
// line below uses default locale if locale is not
// supplied at initialization (constructor argument)
newFormat = NumberFormat.getIntegerInstance(locale);
Since you are setting the locale afterwards, there is no impact on the format pattern assigned to numbers. If you want to use the desire locale in the format for numbers, please use the locale argument at the time of initialization itself e.g. below:
MessageFormat test = new MessageFormat("{0,number,integer}", Locale.FRANCE);
In my opinion, this is a Java bug (the interface is wrong) or a documentation problem. You should open a new issue at Oracle to correct that.
As Yogendra Singh, said the instance of the formatter (DecimalFormat) is created when the MessageFormat constructor.
MessageFormat simpleChoiceTest = new MessageFormat("{0}");
System.out.println(simpleChoiceTest.getFormatsByArgumentIndex()[0]);
//Prints null
MessageFormat explicitChoiceTest = new MessageFormat("{0,number,currency}");
System.out.println(explicitChoiceTest.getFormatsByArgumentIndex()[0]);
//Prints java.text.DecimalFormat#67500
When the MessageFormat.setLocale is called it does not change the locale of its internal formatters.
At least the documentation should be changed to reflect this issue.
That is my java version:
java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b11)

Categories

Resources