I am getting issues with Date Format. I am using SimpleDateFormat to parse date and using MM/dd/yyyy format to parse date, its working properly with correct input like 11/11/2011 and its returning correct result (Fri Nov 11 00:00:00 IST 2011)
But if we enter 11/11/11 as input then its not working properly (Wed Nov 11 00:00:00 IST 11), nor giving parse error.
public static void main(String[] args) {
String format = "MM/dd/yyyy";
String dt = "11/11/11";
Date date = null;
try {
date = TestDate.parDate(dt, format);
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(date);
}
public static Date parDate(String value, String dateFormat) throws ParseException {
Date date = null;
date = new SimpleDateFormat(dateFormat).parse(value);
return date;
}
If you can use Java 8 or above, use DateTimeFormatter and LocalDate classes to parse date values. It will throw an error if the input is not in the expected format.
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("MM-dd-yyyy");
LocalDate date1 = LocalDate.parse("11-11-2011", formatter1);
System.out.println(date1);
Above will work as expected but if you try to parse "11-11-11" with the same formatter object you will get an exception like
Exception in thread "main" java.time.format.DateTimeParseException: Text '11-11-11' could not be parsed at index 6
at java.time.format.DateTimeFormatter.parseResolved0(Unknown Source)
at java.time.format.DateTimeFormatter.parse(Unknown Source)
at java.time.LocalDate.parse(Unknown Source)
at com.is.TestDate.main(TestDate.java:14)
Use MM/dd/yy for both 11/11/11 and 11/11/2011.
If you want to put a strict check, that would not be possible with SimpleDateFormat. You can instead put a pattern check on the date string:
if (!dt.matches("\\d{2}/\\d{2}/\\d{4}")) {
//throw exception
}
It's not an issue. It's correct accroding to Java API of SimpleDateFormat. It says regarding the pattern of Year:
For parsing, if the number of pattern letters is more than 2, the year
is interpreted literally, regardless of the number of digits. So using
the pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
Thus there is no exception thrown as you provided a valid input.
If you have to deal with two different date formats for a year yyyy and yy convert one version to the other or use two formatter - if you have to use SimpleDateFormat at all.
That's the way since Java 8: How to parse/format dates with LocalDateTime? (Java 8)
Related
I have an incoming string which should be a date in format "yyyyMMdd".(e.g today's Date - 20200507)
But sometimes input string can be an invalid format and Date parser should give an exception(parse exception), but it is not except it is returning a date object.
Sample Code in case when string is wrong or alphanumeric like below:
class Demo {
public static void main(String args[]) throws Exception {
String inputString = "9450524Q";
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);
System.out.println(formatter.parse(inputString));
}}
Output:
Tue Apr 04 00:00:00 IST 9454
From the JavaDoc of DateFormat from which SimpleDateFormat directly inherits:
By default, parsing is lenient: If the input is not in the form used by this object's format method but can still be parsed as a date, then the parse succeeds. Clients may insist on strict adherence to the format by calling setLenient(false).
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work. Advantages in your particular case include that the formatter you need is already built in and it does throw the exception that you ask for.
For demonstration I am using this auxiliary method:
public static void tryToParse(String dateString) {
try {
LocalDate date
= LocalDate.parse(dateString, DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(dateString + " -> " + date);
} catch (DateTimeParseException dtpe) {
System.out.println(dateString + ": " + dtpe.getMessage());
}
}
Trying it out:
// Valid date
tryToParse("20200507");
// Invalid date value
tryToParse("20210229");
// Text after date
tryToParse("20200623Q");
// Example from question
tryToParse("9450524Q");
Output is:
20200507 -> 2020-05-07
20210229: Text '20210229' could not be parsed: Invalid date 'February 29' as '2021' is not a leap year
20200623Q: Text '20200623Q' could not be parsed, unparsed text found at index 8
9450524Q: Text '9450524Q' could not be parsed at index 6
Please also enjoy the precise and helpful exception messages. What happened in the last case was: year 9450 and month 52 were parsed, but since 4Q is not a valid two-digit date, the exception was thrown (before validating whether 52 is a valid month number).
What happened in your code
The SimpleDateFormat class is a notorious troublemaker. You have discovered a central design problem with it, but certainly not the only one. What it did was: It parsed 4-digit year, 9450, and two-digit month, 52. There are 12 months in a year, but a SimpleDateFormat with standard settings doesn’t care. It converts 48 of the months to 4 years and ends up in the 4th month 4 years later. Finally 4 is parsed as day of month. The remainder of the text, the Q, is ignored.
As I see it your example exhibits three flaws of SimpleDateFormat:
With standard settings it is lenient, it accepts an invalid month number.
When asked to parse two digits and finding only one, it settles with that one digit without reporting the error.
In the case of unparseable text after the parsed text it does not report any error either.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related questions:
SimpleDateFormat giving wrong date instead of error
Modern answer
SimpleDateFormat parse(string str) doesn't throw an exception when str = 2011/12/12aaaaaaaaa?
Modern answer
This question already has answers here:
SimpleDateFormat parsing date with 'Z' literal [duplicate]
(12 answers)
Closed 4 years ago.
I am using the below code to format millisecond resolution date strings. It works for 2018-09-14T13:05:21.329Z but not 2018-09-14T13:05:21.3Z. Can anybody suggest the reason and how to correct it?
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
SimpleDateFormat sdfDestination = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date parsedDate = formatter.parse(date);
String destDate = sdfDestination.format(parsedDate);
return destDate;
} catch (java.text.ParseException parseException) {
logger.error("Parse Exception occured while converting publication time to date "
+ "format 'yyyy-MM-dd HH:mm:ss'", parseException);
}
I get below exception:
java.text.ParseException: Unparseable date: "2018-09-14T13:05:21.3Z"
at java.text.DateFormat.parse(Unknown Source) ~[na:1.8.0_181]
at com.noordpool.api.implementation.utility.Utility.parseDate(Utility.java:136) [classes/:na]
at com.noordpool.api.implementation.utility.Utility.parseMessage(Utility.java:77) [classes/:na]
Your only problem is that you are using a wrong pattern for SimpleDateFormat, you need to change:
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
To:
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Because the Z used in the date string means "zero hour offset" so you just need to pass it as 'Z' in your pattern.
This is a working demo with the right pattern.
Edit:
And to make things work with different Locales and Timezones, you need to use the appropriate Locale when you are creating the SimpleDateFormat instance, this is how should be the code:
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
The only possible issue I can see is that you're passing in milliseconds incorrectly and the program doesn't know what to do about it.
So the last part of the formatter indicates with milliseconds and a timezone as .SSSX
But how does it evaluate 3Z for the input into this? I mean, do you say it's 300 timezone Z, or say it's 003 timezone Z, or worse, try and parse it as 3Z, which hopefully you see that you cannot turn '3Z' into a number.
To remedy this, I'd validate your input 'date' and ensure the milliseconds part is always 3 digits long, this removes the ambiguity and the program always knows that you mean '300 milliseconds, timezone Z'.
There is a problem in java 8 where the number of characters that you specified with the formatter should be an exact match (which is not specified in the documentation).
You can use three different Formatters and use nested exception as follows:
DateFormat format1 = new SimpleDateFormat("y-M-d'T'H:m:s.SX");
DateFormat format2 = new SimpleDateFormat("y-M-d'T'H:m:s.SSX");
DateFormat format3 = new SimpleDateFormat("y-M-d'T'H:m:s.SSSX");
Date parsedDate;
try {
// Parsing for the case - 2018-09-14T13:05:21.3Z
parsedDate = format1.parse(date);
} catch (ParseException e1) {
try {
// Parsing for the case - 2018-09-14T13:05:21.32Z
parsedDate = format2.parse(date);
} catch (ParseException e2) {
try {
// Parsing for the case - 2018-09-14T13:05:21.329Z
parsedDate = format3.parse(date);
} catch (ParseException e2) {
//The input date format is wrong
logger.error("Wrong format for date - " + date);
}
}
}
java.time
DateTimeFormatter dtfDestination
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
String date = "2018-09-14T13:05:21.3Z";
String destDate = Instant.parse(date)
.atZone(ZoneId.of("Indian/Comoro"))
.format(dtfDestination);
System.out.println(destDate);
Output from this snippet is:
2018-09-14 16:05:21
Please substitute your correct time zone if it didn’t happen to be Indian/Comoro, since correct output depends on using the correct time zone. If you want to use your JVM’s default time zone, specify ZoneId.systemDefault(), but be aware that the default can be changed at any time from other parts of your program or other programs running in the same JVM.
I am exploiting the fact that your string, "2018-09-14T13:05:21.3Z", is in ISO 8601 format, the format that the classes of java.time parse as their default, that is, without any explicit formatter. Instant.parse accepts anything from 0 through 9 decimals on the seconds, so there is no problem giving it a string with just 1 decimal, as you did. In comparison there is no way that an old-fashioned SimpleDateFormat can parse 1 decimal on the seconds with full precision since it takes pattern letter (uppercase) S to mean milliseconds, so .3 will be parsed as 3 milliseconds, not 3 tenths of a second, as it means.
Jahnavi Paliwal has already correctly diagnosed and explained the reason for the exception you got.
The date-time classes that you used, DateFormat, SimpleDateFormat and Date, are all long outdated and SimpleDateFormat in particular is notoriously troublesome. Since you seem to be using Java 8 (and even if you didn’t), I suggest you avoid those classes completely and use java.time instead.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
This question already has answers here:
Java Date Error
(8 answers)
Closed 4 years ago.
I want to convert String values in the format of mm/dd/yy to YYYY-MM-DD Date. how to do this conversion?
The input parameter is: 03/01/18
Code to convert String to Date is given below
public static Date stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
try {
date = formatter.parse(dateVlaue);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
When tried to convert using this method it shows the following error
java.text.ParseException: Unparseable date: "03/01/18"
As you say the input is in a different format, first convert the String to a valid Date object. Once you have the Date object you can format it into different types , as you want, check.
To Convert as Date,
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy");
date = formatter.parse(dateVlaue);
To Print it out in the other format,
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd");
dateString = formatter1.format(date)
You are writing it the wrong way. In fact, for the date you want to convert, you need to write
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy");
The format you are passing to SimpleDateFormat is ("yyyy-MM-dd") which expects date to be in form 2013-03-01 and hence the error.
You need to supply the correct format that you are passing your input as something like below
public static Date stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yy");
try {
date = formatter.parse(dateVlaue);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
The solution for the above problem
Convert the String date value in the Format of "dd/mm/yy" to Date.
By using the converted Date can able to frame the required date format.
The method has given below
public static String stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("dd/mm/yy");
String dateString = null;
try {
// convert to Date Format From "dd/mm/yy" to Date
date = formatter.parse(dateVlaue);
// from the Converted date to the required format eg : "yyyy-MM-dd"
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd");
dateString = formatter1.format(date);
} catch (ParseException e) {
e.printStackTrace();
}
return dateString;
}
EDIT: Your question said “String values in the format of mm/dd/yy”, but I understand from your comments that you meant “my input format is dd/mm/yy as string”, so I have changed the format pattern string in the below code accordingly. Otherwise the code is the same in both cases.
public static Optional<LocalDate> stringToDateLinen(String dateValue) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yy");
try {
return Optional.of(LocalDate.parse(dateValue, dateFormatter));
} catch (DateTimeParseException dtpe) {
return Optional.empty();
}
}
Try it:
stringToDateLinen("03/01/18")
.ifPresentOrElse(System.out::println,
() -> System.out.println("Could not parse"));
Output:
2018-01-03
I recommend you stay away from SimpleDateFormat. It is long outdated and notoriously troublesome too. And Date is just as outdated. Instead use LocalDate and DateTimeFormatter from java.time, the modern Java date and time API. It is so much nicer to work with. A LocalDate is a date without time of day, so this suites your requirements much more nicely than a Date, which despite its name is a point in time. LocalDate.toString() produces exactly the format you said you desired (though the LocalDate doesn’t have a format in it).
My method interprets your 2-digit year as 2000-based, that is, from 2000 through 2099. Please think twice before deciding that this is what you want.
What would you want to happen if the string cannot be parsed into a valid date? I’m afraid that returning null is a NullPointerException waiting to happen and a subsequent debugging session to track down the root cause. You may consider letting the DateTimeParseException be thrown out of your method (just declare that in Javadoc) so the root cause is in the stack trace. Or even throw an AssertionError if the situation is not supposed to happen. In my code I am returning an Optional, which clearly signals to the caller that there may not be a result, which (I hope) prevents any NullPointerException. In the code calling the method I am using the ifPresentOrElse method introduced in Java 9. If not using Java 9 yet, use ifPresent and/or read more about using Optional elsewhere.
What went wrong in your code?
The other answers are correct: Your format pattern string used for parsing needs to match the input (not your output). The ParseException was thrown because the format pattern contained hyphens and the input slashes. It was good that you got the exception because another problem is that the order of year, month and day doesn’t match, neither does the number of digits in the year.
Link
Oracle tutorial: Date Time explaining how to use java.time.
I used the follow code to parse datetime:
String parseDate(String tranDateTime, String originalDateFormat, String toDateFormat) throws ParseException {
SimpleDateFormat originalMonthYear = new SimpleDateFormat(originalDateFormat);
Date date = originalMonthYear.parse(tranDateTime);
SimpleDateFormat formatter = new SimpleDateFormat(toDateFormat);
return formatter.format(date);
}
And tried to parse a datetime value as below :
parseDate("2017-08-16T00:00:00Z",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", "yyyy-MM-dd HH:mm:ss");
An exception is thrown :
java.text.ParseException: Unparseable date: "2017-08-16T00:00:00Z"
However, when I change date value to "2017-08-16T02:54:15.537Z", the function worked fine. I don't know why.
The problem is coming from :
Date date = originalMonthYear.parse(tranDateTime);
This date
2017-08-16T00:00:00Z
Doesn't match the pattern
yyyy-MM-dd'T'HH:mm:ss.SSSXXX
Because milliseconds are missing (SSS part)
So
Date date = originalMonthYear.parse(tranDateTime);
Throws the exception
java.text.ParseException: Unparseable date: "2017-08-16T00:00:00Z"
Solutions
Change your date 2017-08-16T00:00:00Z into 2017-08-16T00:00:00.000Z (add milliseconds to your date)
Change your pattern yyyy-MM-dd'T'HH:mm:ss.SSSXXX into yyyy-MM-dd'T'HH:mm:ssXXX (remove milliseconds from your pattern)
You asked in a comment:
Moreover, the datetime value could be "2017-08-16T00:00:00Z" or
"2017-08-16T00:00:15.537Z" retrieved from database. Do we have a
general pattern to parse both formats?
Even better, they can both be parsed without an explicit pattern:
System.out.println(OffsetDateTime.parse("2017-08-16T00:00:00Z"));
System.out.println(OffsetDateTime.parse("2017-08-16T00:00:15.537Z"));
This prints:
2017-08-16T00:00Z
2017-08-16T00:00:15.537Z
To format, for example:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(OffsetDateTime.parse("2017-08-16T00:00:00Z").format(formatter));
This prints
2017-08-16 00:00:00
I am using and warmly recommending java.time, the modern Java date and time API. SimpleDateFormat and Date are long outmoded and the former in particular notoriously troublesome. The modern API is so much nicer to work with.
Your strings (in spite of differences) both conform to ISO 8601, the standard for date and time formats. The java.time classes parse these formats without any explicit formatter.
Link: Oracle tutorial: Date Time explaining how to use java.time
Change 2017-08-16T00:00:00Z to 2017-08-16T00:00:00.000Z, it works for me.
Problem source :
The problem come from trying to pars date value 2017-08-16T00:00:00Z with a date format yyyy-MM-dd'T'HH:mm:ss.SSSXXX, in the line :
Date date = originalMonthYear.parse(tranDateTime);
See the difference between the value and format:
______________________________
|2017|08|16| T |00|00|00Z |
|yyyy|MM|dd|'T'|HH|mm|ss.SSSXXX|
|____|__|__|___|__|__|_________|
Solution :
Simple one is :
Date date = originalMonthYear.parse("2017-08-16T00:00:00.000Z")
But, if you will get many differents formats, you can use something like (never tested):
List<SimpleDateFormat> myUsedPatterns = new ArrayList<>();
myUsedPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
myUsedPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"));
for (SimpleDateFormat myPattern : myUsedPatterns) {
try {
SimpleDateFormat originalMonthYear = new SimpleDateFormat(myPattern );
Date date = originalMonthYear.parse(tranDateTime);
SimpleDateFormat formatter = new SimpleDateFormat(toDateFormat);
return formatter.format(date);
} catch (ParseException e) {
// Loop
}
}
I'm having huge difficulties with simple date format. First of all, I know not all tutorials on all sites are actually good, but the fact that all non trivial formats (not dd/MM/yyyy) gave a parse exception (plus my own tests don't work as expected) is rather frustrating to me.
This is the site in question: http://www.mkyong.com/java/how-to-convert-string-to-date-java/
And I don't understand why something as simple as:
private static void unexpectedFailure() {
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy");
String dateInString = "7-Jun-2013";
try {
Date date = formatter.parse(dateInString);
System.out.println(date);
System.out.println(formatter.format(date));
} catch (ParseException e) {
e.printStackTrace();
}
}
Throws a parse exception.
Also besides that, I'm trying to parse my own dates. This code gives strange results (unexpected I would say):
public static void doSomething(List<String> list) {
Iterator<String> iter = list.iterator();
String[] line = iter.next().split(" ");
System.out.println(Arrays.toString(line));
DateFormat format = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
format.setLenient(true);
try {
System.out.println(line[0]+" "+line[1]);
System.out.println(format.parse(line[0]+" "+line[1]));
} catch (ParseException e) {
System.out.println("In theory this should not get caught.");
}
}
Prints out this:
[06/08/2015, 13:51:29:849, DEBUG, etc...]
06/08/2015 13:51:29:849
Thu Aug 06 13:51:29 EEST 2015
Thu Aug 06 13:51:29 EEST 2015 WHAT? WHY?
EDIT I'll try and explain. In my last code snippet I'm just trying to determine if the string is a date, and it passes "the test". However when I'm printing it out the format is simply bizzare. I'm starting to think that is because I'm printing a date. How can I even print a DateFormat? What I was expecting was dd/MM/yyyy hh:mm:ss not ddd MMM 06? hh:mm:ss G YYYY
And I don't understand why something as simple as:
(code snipped)
Throws a parse exception.
My guess is that it's tripping up over Jun which may not be a valid month abbreviation in your system default locale. I suggest you specify the locale in your SimpleDateFormat constructor:
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
Then you're dealing with a locale which definitely has Jun as a month abbreviation.
That said, where possible I'd suggest using numeric formats where possibly, ideally ones following ISO-8601, e.g.
yyyy-MM-dd'T'HH:mm:ss
However when I'm printing it out the format is simply bizzare.
No it's not. You're effectively using
Date date = format.parse(line[0]+" "+line[1]);
System.out.println(date);
So that's calling Date.toString(), documented as:
Converts this Date object to a String of the form:
dow mon dd hh:mm:ss zzz yyyy
So that's working as expected. However, you want to format the date using your SimpleDateFormat - so you need to call format:
System.out.println(format.format(date));
Of course that just checks that it can round-trip, basically.
As a side note, I suspect you want HH (24-hour clock) instead of hh (12-hour clock) in your format string.