My date drives me crazy (Java) - java

I need to convert a string to a Date object using SimpleDateFormat.
I use "MMM. dd yyyy" to parse strings like "Dec. 30 2011", with 3 month char and a dot.
It fails for "May 30 2011" so I have to catch the exception and re-try with "MMM dd yyyy". It works for "May", so far so ugly but works.
But when it get to "Sept. 11 2011", all of the above plus "MMMM. dd yyyy" and "MMMM dd yyyy" fail. So I can't parse it at all.
What is going on?

try this pattern -
MMM'.' dd yyyy
Code -
DateFormat df = new SimpleDateFormat("MMM'.' dd yyyy");
Date date = df.parse("Dec. 30 2011");
Hope it will help you to make fun :)

If it were me, instead of trying different patterns and rely on exception handling, I would just substring the first 3 letters, append the last 7 letters, and parse it with MMMdd yyyy.

I think there is no out-of-the-box solution for tolerant date parsing. Neither JDK nor the often used Joda Time library support this directly. But it should be relatively easy to write a helper method that tries different date formats until it succeeds (or fails).
private static final DateFormat[] FORMATS = new DateFormat[] {
new SimpleDateFormat("MM. dd yy"),
new SimpleDateFormat("MMM. dd yy"),
new SimpleDateFormat("MMM dd yy") };
public static synchronized Date parse(String dateString) throws ParseException {
ParseException e = null;
for(DateFormat format : FORMATS) try {
return format.parse(dateString);
} catch (ParseException ex) {
if (e == null || e.getErrorOffset() < ex.getErrorOffset()) e = ex;
}
throw e;
}

Java can only parse 3-letter abbreviations (as MMM) or full month names (as MMMM).
You must massage your input into a 3-letter abbreviation. The simplest way is using regex.
Execute this line before parsing to clean it up:
str = str.replaceAll("(?<=^...)\\w+\\.?", "");
This will give up you Sep 11 2011 from Sept. 11 2011, but leave already appropriate input untouched. It will also clean up any length abbreviation, with or without the full stop - it's versatile.
You can then parse it using you existing format string.

Related

ParseException trying to parse Date from string with + in it

I have a problem to parse my date from string
This is my date
String startedFrom = "Fri,+31+Dec+3999+23:00:00+GMT"
DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss z", Locale.ENGLISH);
Date result = df.parse(startedFrom);
what I am doing wrong?
I get exception
java.text.ParseException: Unparseable date: "Fri,+31+Dec+3999+23:00:00+GMT"
DateFormat df = new SimpleDateFormat("EEE,'+'dd'+'MMM'+'yyyy'+'kk:mm:ss'+'z",
Locale.ENGLISH);
However if the startedFrom value is actually an URL encoded parameter added to an URL (as in a HTML form with GET method), then '+' would arrive as space ' ', hence your original format would be correct.
First, do use java.time and its DateTimeFormatter class for this. SimpleDateFormat is notoriously troublesome and is long outdated along with the Date class. java.time is the modern Java date and time API and is so much nicer to work with.
Second, Joop Eggen is correct in his answer that your string looks like a URL encoded parameter that was originally Fri, 31 Dec 3999 23:00:00 GMT. This sounds even more likely as this is a standard format known as RFC 1123 and commonly used with HTTP. So your library for getting your URL parameters should URL decode the string for you. Then it’s straightforward since the formatter to use has already been defined for you:
String startedFrom = "Fri, 31 Dec 3999 23:00:00 GMT";
OffsetDateTime result
= OffsetDateTime.parse(startedFrom, DateTimeFormatter.RFC_1123_DATE_TIME);
System.out.println(result);
This prints
3999-12-31T23:00Z
If you cannot get your library to URL decode, do it yourself by using URLDecoder:
String startedFrom = "Fri,+31+Dec+3999+23:00:00+GMT";
try {
startedFrom = URLDecoder.decode(startedFrom, StandardCharsets.UTF_16.name());
} catch (UnsupportedEncodingException uee) {
throw new AssertionError("UTF_16 is not supported — this should not be possible", uee);
}
Now proceed as above.
You may of course also define a formatter to parse the string with the pluses in it. I don’t know why you should want to, though. If you do, you just need to have them in the format pattern string too:
DateTimeFormatter formatterWithPluses
= DateTimeFormatter.ofPattern("EEE,+d+MMM+yyyy+H:mm:ss+z", Locale.ROOT);
ZonedDateTime result = ZonedDateTime.parse(startedFrom, formatterWithPluses);
This time we got a ZonedDateTime with GMT as the name of the time zone:
3999-12-31T23:00Z[GMT]
Depending on what you need the date-time for, you may convert it to OffsetDateTime or Instant by calling result.toOffsetDateTime() or result.toInstant().

SimpleDateFormat: unexpected results and unexpected parse exceptions

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.

Convert String to Date error parsing

I have looked at many examples and can still not find an answer to match my problem. I have now been stuck for an hour and need help.
I have many strings that are formated like this:
Wed, 06 Nov 2013 18:14:02
I have created a function to convert these strings into Date:
private static Date toDate(String pubDateString) {
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
pubDateString = pubDateString.substring(0, 25);
Date date = null;
try {
date = dateFormat.parse(pubDateString);
} catch (ParseException e1) {
e1.printStackTrace();
}
return date;
}
What I get is a ParseException:
java.text.ParseException: Unparseable date: "Wed, 06 Nov 2013 18:14:02"
Could anyone help me on my first challenge? Thanks.
edit : I tried HH, and kk
(When I originally answered the question, the format string used hh - it has since been changed.)
You're using hh for the hours part, which is a 12-hour format - but providing a string which uses "18". You want HH.
Additionally, I'd suggest explicitly specifying the locale if you know that the values will always use English names.
I've verified that if you specify the locale explicitly, the code definitely works - at least under Oracle's Java 7 implementation:
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss",
Locale.US);
If it wasn't working for you without the locale being specified (but with HH) that's probably why - presumably your system locale isn't English, so it was expecting different month and day names.

how to verify this time/date string in java?

I have a string in the format of:
3:00 pm on Aug 28
What would be the best way to verify that a valid time and valid date is contained within this string? My first thought was to split the string and use two regexs to match a time and the other one to match that specfic date format (abbreviate month day). However I'm having a little bit of trouble with the second regex (the one for the specfic date format). How else could one go about verifying the string is in the correct format?
You can try this:
public boolean isValid( String dateStr ) {
// K: hour of the day in am/pm
// m: minute of a hour
// 'on': static text
// MMM: name of the month with tree letters
// dd: day of the month (you can use just d too)
DateFormat df = new SimpleDateFormat( "K:m a 'on' MMM dd", Locale.US );
try {
df.parse( dateStr );
return true;
} catch ( ParseException exc ) {
}
return false;
}
More about the format string here: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
Use java.text.SimpleDateFormat. Use a format string something like HH:mm aa 'on' MMM dd.
You may have to add yyyy to the format string and 2012 to your input.
Use SimpleDateFormat and make sure it doesn't use lenient parsing:
try {
DateFormat df = new SimpleDateFormat("h:mm a 'on' MMM dd", Locale.US);
df.setLenient(false);
Date dt = df.parse(s);
} catch (ParseException pe) {
// Wrong format
}

Parsing dates of the format "January 10th, 2010" in Java? (with ordinal indicators, st|nd|rd|th)

I need to parse the dates of the format "January 10th, 2010" in Java. How can I do this?
How to handle the ordinal indicators, the st, nd, rd, or th trailing the day number?
This works:
String s = "January 10th, 2010";
DateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy");
System.out.println("" + dateFormat.parse(s.replaceAll("(?:st|nd|rd|th),", "")));
but you need to make sure you are using the right Locale to properly parse the month name.
I know you can include general texts inside the SimpleDateFormat pattern. However in this case the text is dependent on the info and is actually not relevant to the parsing process.
This is actually the simplest solution I can think of. But I would love to be shown wrong.
You can avoid the pitfalls exposed in one of the comments by doing something similar to this:
String s = "January 10th, 2010";
DateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy");
System.out.println("" + dateFormat.parse(s.replaceAll("(?<= \\d+)(?:st|nd|rd|th),(?= \\d+$)", "")));
This will allow you to not match Jath,uary 10 2010 for example.
I should like to contribute the modern answer. Rather than the SimpleDateFormat class used in the two top-voted answer today you should use java.time, the modern Java date and time API. It offers a couple of nice solutions.
Easy solution
We first define a formatter for parsing:
private static final DateTimeFormatter PARSING_FORMATTER = DateTimeFormatter.ofPattern(
"MMMM d['st']['nd']['rd']['th'], uuuu", Locale.ENGLISH);
Then we use it like this:
String dateString = "January 10th, 2010";
LocalDate date = LocalDate.parse(dateString, PARSING_FORMATTER);
System.out.println("Parsed date: " + date);
Output is:
Parsed date: 2010-01-10
The square brackets [] in the format pattern string enclose optional parts, and the single quotes enclose literal text. So d['st']['nd']['rd']['th'] means that there may be st, nd, rd and/or th after the day of month.
More solid solution
A couple of limitations with the approach above are
It accepts any ordinal indicator, for example 10st and even 10stndrdth.
While the formatter works for parsing, you cannot use it for formatting (it would give January 10stndrdth, 2010).
If you want better validation of the ordinal indicator or you want the possibility of formatting the date back into a string, you may build your formatter in this way:
private static final DateTimeFormatter FORMATTING_AND_PARSING_FORMATTER;
static {
Map<Long, String> ordinalNumbers = new HashMap<>(42);
ordinalNumbers.put(1L, "1st");
ordinalNumbers.put(2L, "2nd");
ordinalNumbers.put(3L, "3rd");
ordinalNumbers.put(21L, "21st");
ordinalNumbers.put(22L, "22nd");
ordinalNumbers.put(23L, "23rd");
ordinalNumbers.put(31L, "31st");
for (long d = 1; d <= 31; d++) {
ordinalNumbers.putIfAbsent(d, "" + d + "th");
}
FORMATTING_AND_PARSING_FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("MMMM ")
.appendText(ChronoField.DAY_OF_MONTH, ordinalNumbers)
.appendPattern(", uuuu")
.toFormatter(Locale.ENGLISH);
}
This will parse the date string the same as the one above. Let’s also try it for formatting:
System.out.println("Formatted back using the same formatter: "
+ date.format(FORMATTING_AND_PARSING_FORMATTER));
Formatted back using the same formatter: January 10th, 2010
Links
Oracle tutorial: Date Time explaining how to use java.time.
My answer to a question about formatting ordinal indicators from which I took the more solid formatter.
You can set nd etc as literals in a SimpleDateFormat. You can define the four needed format and try them. Starting with th first, because I guess this will occur more often. If it fails with ParseException, try the next one. If all fail, throw the ParseException. The code here is just a concept. In real-life you may would not generate the formats new everytime and may think about thread-safety.
public static Date hoolaHoop(final String dateText) throws ParseException
{
ParseException pe=null;
String[] sss={"th","nd","rd","st"};
for (String special:sss)
{
SimpleDateFormat sdf=new SimpleDateFormat("MMMM d'"+special+",' yyyy");
try{
return sdf.parse(dateText);
}
catch (ParseException e)
{
// remember for throwing later
pe=e;
}
}
throw pe;
}
public static void main (String[] args) throws java.lang.Exception
{
String[] dateText={"January 10th, 2010","January 1st, 2010","January 2nd, 2010",""};
for (String dt:dateText) {System.out.println(hoolaHoop(dt))};
}
Output:
Sun Jan 10 00:00:00 GMT 2010
Fri Jan 01 00:00:00 GMT 2010
Sat Jan 02 00:00:00 GMT 2010
Exception in thread "main" java.text.ParseException: Unparseable date: ""
"th","nd","rd","st" is of course only suitable for Locales with english language. Keep that in mind. In france, "re","nd" etc I guess.
This is another easy way ,but need to include apache commons jar.
import org.apache.commons.lang.time.*;
String s = "January 10th, 2010";
String[] freakyFormat = {"MMM dd'st,' yyyy","MMM dd'nd,' yyyy","MMM dd'th,' yyyy","MMM dd'rd,' yyyy"};
DateUtils du = new DateUtils();
System.out.println("" + du.parseDate(s,freakyFormat));

Categories