What is wrong with this java date formatter? - java

I'm trying to format the date you see as a String in the below code, to be able to have it as a LocalDateTime object, but I'm getting an exception, I'm following this guide https://mkyong.com/java8/java-8-how-to-convert-string-to-localdate/, but unfortunately it doesn't have and example like the date I have below, can someone please give me a hand here? I would really appreciate :)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE MMM d hh:mm:ss zzz yyyy", Locale.US);
String date = "Wed Nov 18 00:00:00 COT 2020";
LocalDateTime localDateTime = LocalDateTime.parse(date, formatter);
System.out.println(localDateTime);
System.out.println(formatter.format(localDateTime));
I am getting:
java.time.format.DateTimeParseException: Text 'Wed Nov 18 00:00:00 COT
2020' could not be parsed at index 0

If you want round trip parsing/formatting with Zones try
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
String date = "Wed Nov 18 00:00:00 COT 2020";
ZonedDateTime localDateTime = ZonedDateTime.parse(date, formatter);
System.out.println(localDateTime);
System.out.println(formatter.format(localDateTime));

To answer the question as asked in the title, What is wrong with this Java date formatter?
EEEE is for full name of the day of the week like Monday. For the abbreviation like Mon you need either E, EE or EEE.
As others have said, lower case hh is for clock hour within AM or PM from 01 through 12, so cannot parse your hour of 00. And even if it could, it would not provide enough information for the time of day. For hour of day from 00 though 23 you need upper case HH.
There’s a more basic problem that Basil Bourque already mentioned in a comment: The result of parsing a time zone abbreviation like COT is generally undefined. While COT may have only one definition, I don’t know, most of the most common abbreviations are ambiguous, and you don’t know what you get from parsing them.
As has also been mentioned your formatter cannot be used for formatting a LocalDateTime. A LocalDateTime hasn’t got any time zone. The formatter requires a time zone for the abbreviated time zone name, zzz. You may either format a ZonedDateTime, or you may modify the formatter to have an override zone using its withZone method.
A tip: When you don’t know why parsing fails, try formatting the expected value with the same formatter and compare the result to the string you are trying to parse. Most often the difference will lead you on the right track. Like this:
ZonedDateTime val = ZonedDateTime.of(
2020, 11, 18, 0, 0, 0, 0, ZoneId.of("America/Bogota"));
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("EEEE MMM d hh:mm:ss zzz yyyy", Locale.US);
String toBeParsed = "Wed Nov 18 00:00:00 COT 2020";
String formatted = val.format(formatter);
System.out.println("String to be parsed: " + toBeParsed);
System.out.println("Formatted string: " + formatted);
Output:
String to be parsed: Wed Nov 18 00:00:00 COT 2020
Formatted string: Wednesday Nov 18 12:00:00 COT 2020
The difference are Wednesday and 12, so it seems the bugs in the pattern are at EEEE and hh.
Your string seems to come out of the toString method of the outdated java.util.Date class. To parse the string from there see one of the answers linked to at the bottom. Or still better, get hold of the Date object, convert it to a modern Instant using its toInstant method and perform any further conversions from there. Or yet still better, stop using Date completely.
Links
How to parse the result from Date.toString():
Answer by Arvind Kumar Avinash
Answer by Arvind Kumar Avihash
Answer by Arvind Kumar Avinash
My answer
Answer by Basil Bourque
And of course the answer by Scary Wombat to this question

Related

Java - parse date with AM/PM next to seconds (no space)

I am trying to parse the following kind of date: Dec 12 2001 11:59:59PM.
If the AM/PM wasn't next to the seconds, I would use the following pattern: MMM dd yyyy HH:mm:ss a.
However the AM/PM is ignored with the pattern MMM dd yyyy HH:mm:ssa (and is therefore always interpreted as AM).
I am trying to use a SimpleDateFormat. I tried to specify a locale to no avail.
Is this possible using a SimpleDateFormat or do I need to use an alternative method/external processing? The SimpleDateFormat particularly interests me due to its use in the pattern attribute of the #JsonFormat annotation.
Thanks.
I would like to use java.time API from Java8+ instead of the old Date :
String date = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm:ssa", Locale.ENGLISH)
);
or :
DateTimeFormatter format = DateTimeFormatter.ofPattern(
"MMM dd yyyy hh:mm:ssa", Locale.ENGLISH
);
String date = LocalDateTime.of(2001, Month.DECEMBER, 12, 11, 59, 59).format(format);
Outputs
Jun 14 2018 03:01:02PM
Dec 12 2001 11:59:59AM
with AM/PM you want 12 hours hh instead of 24 hours HH.
hh:mm:ss''a
As k/K/h/H influence a too, now everything might work with ssa.
If ssa is still problematic (seemingly a bug), try separating the letters by an empty literal string (single quotes).
The following works:
hh:mm:ssa
It may very well be possible with SimpleDateFormat, but you will probably prefer to use java.time, the modern Java date and time API:
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("MMM dd uuuu hh:mm:ssa", Locale.ENGLISH);
String dateTimeString = "Dec 12 2001 11:59:59PM";
LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, formatter);
System.out.println(dateTime);
Output:
2001-12-12T23:59:59
As others have said, your problem was not with the lack of a space between seconds and AM/PM marker, but with using uppercase HH for the hours. Uppercase HH is for hour of day from 00 through 23, where what you wanted was lowercase hh for hour within AM or PM from 01 through 12.
And as yet others have said, there are issues with using SimpleDateFormat and its friend Date:
Those classes are long outdated.
Those classes are poorly designed, and SimpleDateFormat in particular is renowned for being troublesome. Your experience is typical and certainly not unusual.
Getting a correct result from SimpleDateFormat requires that either the JVM time zone setting agrees with the time zone understood in the string, or you set the time zone of the SimpleDateFormat to the relevant time zone. The former is hard to guarantee since the time zone setting can be changed any time from another part of your program or from other programs running in the same JVM.
This also means that if you do require an instance of the outdated Date class (for example for a legacy API that you don’t want to change just now), you will need to decide on a time zone for the conversion. Then convert for example like this:
Instant inst = dateTime.atZone(ZoneId.of("America/Metlakatla")).toInstant();
Date oldfashionedDate = Date.from(inst);
System.out.println(oldfashionedDate);
I hesitate to show you the output because Date shows a quite surprising behaviour here.
Thu Dec 13 08:59:59 CET 2001
08:59? On December 13? The conversion has given you the correct point in time. When I print the Date, its toString method is invoked. This in turn uses my JVM’s time zone setting for producing the string, so the output is in a completely different time zone from the one where the conversion happened. So apparently when it’s 23:59 in Metlakatla, it’s already 08:59 the next day in Copenhagen (my time zone; CET in the output is for Central European Time). Had my JVM’s time zone setting been America/Metlakatla too, the output would have agreed more with the expected:
Wed Dec 12 23:59:59 AKST 2001
java.time is more helpful
What you asked SimpleDateFormat to do was to parse a time that had hour of day 11 and PM. This is really self contradictory since PM only begins at hour of day 12. So it would be reasonable to expect an exception from the request. A SimpleDateFormat with standard settings doesn’t give you that. It’s very typical for SimpleDateFOrmat to give you a wrong result and pretend all is well. However let’s for a moment try my modern code with your format pattern string of MMM dd yyyy HH:mm:ssa. Then we get:
Exception in thread "main" java.time.format.DateTimeParseException:
Text 'Dec 12 2001 11:59:59PM' could not be parsed: Conflict found:
Field AmPmOfDay 0 differs from AmPmOfDay 1 derived from 11:59:59
I don’t claim I understand exactly why it is worded like this, but it is mentioning a conflict in the AM/PM, which is exactly what we have.
PS
I hadn’t thought at first that I’d contribute an answer, but in the end I was provoked by on one hand bohemian’s comment that only Joop Eggen’s answer was correct and on the other hand a couple of comments by Basil Bourque claiming that you could not use the SimpleDateFormat that Joop Eggen was using. So I wanted to set things straight.
Link
Oracle tutorial: Date Time explaining how to use java.time.
Java internally uses the builder pattern. This is slightly modified from the source code of DateTimeFormatter.RFC_1123_DATE_TIME:
I don't recommend using this over the alternative, DateTimeFormatter.ofPattern(), but it can prove more powerful in certain scenarios where you hit the limitations of ofPattern.
Map<Long, String> moy = new HashMap<>();
moy.put(1L, "Jan"); moy.put(2L, "Feb"); moy.put(3L, "Mar");
moy.put(4L, "Apr"); moy.put(5L, "May"); moy.put(6L, "Jun");
moy.put(7L, "Jul"); moy.put(8L, "Aug"); moy.put(9L, "Sep");
moy.put(10L, "Oct"); moy.put(11L, "Nov"); moy.put(12L, "Dec");
DateTimeFormatter format = new DateTimeFormatterBuilder()
.appendText(MONTH_OF_YEAR, moy)
.appendLiteral(' ')
.appendValue(DAY_OF_MONTH, 2)
.appendLiteral(' ')
.appendValue(YEAR, 4, 4, EXCEEDS_PAD)
.appendLiteral(' ')
.appendValue(HOUR_OF_AMPM, 1, 2, NOT_NEGATIVE)
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 1, 2, NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 1, 2, NOT_NEGATIVE)
.optionalEnd()
.appendText(AMPM_OF_DAY)
.toFormatter();
System.out.println(format.parse("Jun 14 2018 2:51:22AM")); // {},ISO resolved to 2018-06-14T02:51:22
System.out.println(format.parse("Jun 14 2018 2:51:22PM")); // {},ISO resolved to 2018-06-14T14:51:22
Note
Unfortunately, the below code is inaccurate. AM/PM marker is never read. Depending on the local time, either AM or PM is assumed.
Original answer
Use the following format string "MMM dd yyyy HH:mm:ssaaa".
String str = "Jun 14 2018 13:53:19PM";
DateFormat df = new SimpleDateFormat("MMM dd yyyy HH:mm:ssaaa");
try {
Date date = df.parse(str);
System.out.print(df.format(date));
} catch (ParseException e) {
e.printStackTrace();
}

Confused with Converting the date with SimpleDateFormat [duplicate]

This question already has answers here:
How to convert "Mon Jun 18 00:00:00 IST 2012" to 18/06/2012?
(5 answers)
Closed 5 years ago.
I have a problem with date converting. I use the following program and I expect the output: 19.05.2017
But the output is: 05.00.2017
Can anybody help?
String t = "Fri May 19 00:00:00 CEST 2017";
Date d = new SimpleDateFormat("EEE MMM DD hh:mm:ss zzzz YYYY", Locale.US).parse(t);
String s = new SimpleDateFormat("dd.mm.yyyy").format(d).toString();
System.out.println(s);
A surprising result. The oldfashioned classes SimpleDateFormat and friends are full of surprises. This is meant as a negative thing.
Uppercase DD is day of year. Lowercase hh is hour of AM or PM (1 through 12). Uppercase YYYY is weekbased year (only useful with week number). So you are asking for a date that is a Friday in May and the 19th day of the year. Obviously this is not possible.
The result of parsing is Thu Jan 05 23:00:00 CET 2017. Apparently SimpleDateFormat opts for giving you a Friday and for using the zone offset of 2 hours implied by CEST even though the date it has chosen is not at the time of year where CEST (summer time) is in use. I don’t know whether it just gives you the first Friday of the weekbased year (Friday in week 1 of the year). Friday at 0000 hours at offset GMT+2 equals Thursday at 23 at GMT+1, which is CET.
Next for the formatting, 05 is the date as expected, but lowercase mm means minutes. Since the minutes are 0, you get 00. You got the right year.
Rather than using the outdated classes that give you such surprises, I agree with Sam’s answer that you should use the newer classes in java.time:
ZonedDateTime dt = ZonedDateTime.parse(t,
DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US));
String s = dt.format(DateTimeFormatter.ofPattern("dd.MM.uuuu"));
This code gives you 19.05.2017 as you had expected. One of the good things about the modern classes is, if you try to parse with your original format pattern string, you will get a DateTimeParseException so you will know something is wrong. I certainly prefer an exception over incorrect output.
Another good thing is these classes respect the time zone in the input and use it in the output too (unless you explicitly instruct them otherwise). They will never turn Friday 6 January into Thursday 5 January because of some funny time zone issue.
Your input date is in Central European Summer Time and your date format is a bit wrong. Try
SimpleDateFormat input = new SimpleDateFormat("EEE MMM dd hh:mm:ss zzzz yyyy");
You might want to set the timezone on the output date format in order to get the date in the correct local time.
Ideally you'd move over to use a java.time style as shown here:
https://www.mkyong.com/java/java-convert-date-and-time-between-timezone/

Convert Date String Mon Mar 30 13:51:35 UTC 2015 to Date Object

I am trying to convert a string such as
String dateString = "Mon Mar 30 13:51:35 UTC 2015";
in a Date Object.
I tried this:
DateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss ZZZ yyyy", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println("Date Object:"+sdf.parse(dateString));
But the output of the date object is
Mon Mar 30 15:51:35 CEST 2015
as you can see:
1) it forwards the string's time ahead to two hours
2) it changes UTC --> CEST
I tried many solutions, but nothing worked. What is the correct way to do this?
EDIT: my objective here is to have a Date object from that original String. That Date Object should have the same parameters as the date string. In this case, the original hours of day (13) is turned to 15, but the desired is for it to stay at 13. I need this because in my program I will need to compare two different date objects.
EDIT: JAVA 8 SOLUTION
Searching the more recent Java 8, I found a better and more elegant solution. Here is the code
String pattern = "EEE MMM dd HH:mm:ss SSS zzz yyyy";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, Locale.UK).withZone(ZoneId.of("UTC"));
final ZonedDateTime parsed = ZonedDateTime.parse(dateString, formatter);
Furthermore, to compare it with, for example, the current time:
ZonedDateTime now = ZonedDateTime.now();
int compared = parsed.compareTo(now);
System.out.println("NOW:"+now.toLocalDateTime()+" PARSED:"+parsed.toLocalDateTime()+" COMPARED:"+compared);
You are doing it correctly. The date is being parsed correctly. You are just printing the date into your local computer timezone. When you do toString() to a date, prints the date in your local machine timezone.
Mon Mar 30 15:51:35 CEST 2015 == Mon Mar 30 13:51:35 UTC 2015
CEST is UTC +2
A java.util.Date does not have a time zone, practically speaking. There is a time zone inside but it cannot be set nor gotten. One of many poor design decisions made in these old date-time classes.
The Date::toString method applies your JVM’s current default time zone when generating the output string. Done with good intentions, but not helpful as it creates the illusion your Date object is in that zone when in fact it is not.
java.time
You are using a troublesome old legacy class, now supplanted by the java.time framework built into Java 8 and later.
Convert from a Date to an Instant, a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = myJavaUtilDate.toInstant();
Call toString. The java.time classes use standard ISO 8601 formats when parsing/generating strings.
String output = instant.toString();
To create strings, convert from Instant to OffsetDateTime using the constant ZoneOffset.UTC. Then work with the java.time.format classes to generate the string.
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , ZoneOffset.UTC );
Search Stack Overflow for more info and examples. These issues have addressed hundreds of times already.
Instead of UTC, use GMT when getting the timezone.
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Source
You already have the Date object. If you want to print it in format you want, you can use DateFormat to format the Date object as well:
Date date = sdf.parse(dateString);
System.out.println("Date Object:"+sdf.format(date));
// Use the date object ...

Java SimpleDateFormat parsing irregularities

I'm doing some date parsing in Java and am encountering some weird behavior.
I have a date string such as follows:
String s = "Sun Aug 11 2013 11:00:00 -0700 (Pacific Daylight Time)"
I'm trying to parse it into a date object like so:
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss Z (zzzz)");
I then print out the resulting date object from sdf.parse(s) and get:
Sun Aug 11 12:00:00 CDT 2013
I am in the central time zone, so it makes sense that it prints it as such, however, CDT is -0500, so the parsed date should be 13:00, not 12:00.
The odd thing is, if I remove either of the redundant pieces of time zone information, the date parses correctly. Using the format "EEE MMM dd yyyy HH:mm:ss Z ('Pacific Daylight Time')" or the format "EEE MMM dd yyyy HH:mm:ss '-0700' (zzzz)" results in the correct date:
Sun Aug 11 13:00:00 CDT 2013
This behavior seems to only occur with dates that fall within daylight savings time. If I instead parse a date in, say, December, with my initial date format, I get the correct result.
I have somewhat limited control over the format of the dates I'm parsing, and they could be coming from a variety of time zones. Has anyone encountered this behavior before, and is there a way to get around it without changing the format of the date string? I realize the time zone designations are redundant, but they aren't incorrect as far as I can tell.
There have certainly been bugs in Java's handling of daylight saving time and time zones in the past, and this sure looks like one you've found. What version of Java is this?
You might want to try giving Joda-Time a try to see if it handles the given date correctly.
If Joda doesn't help, you might need to try pre-parsing some of that date string to remove the descriptive time zone in parenthesis since it works when only one is defined. Very strange indeed!

How can I use Java's SimpleDateFormat to parse a timezone given as "GMT+0100 (BST)"?

I have a date that's in the form of:
Wed Aug 17 2011 09:57:09 GMT+0100 (BST)
and have a filter that takes a time in a certain format. The problem seems to be the time zone on the end, none of the format strings I'm putting in the filter seem to work for this type of date format.
For example,
Wed Aug 17 2011 09:57:09 GMT+0100 (BST)
EEE MMM dd yyyy HH:mm:ss zZ?
The time zone part of this, keeps throwing an error.
Can anyone tell me what the correct format to parse the time zones on these dates is?
"z" needs a colon between hours and minutes. "Z" is only +/-HHMM (i.e. no "GMT" prefix).
One way to parse it is: EEE MMM dd yyyy HH:mm:ss 'GMT'Z. The "BST" bit is ignored, and it's based on assumption that there's always "GMT" before offset.
I would parse out and interpret the time zone information separately, then use that to construct the Date/Calendar object in the proper time zone.
The following code seems to work well enough with your example:
String source = "Wed Aug 17 2011 09:57:09 GMT+0100 (BST)";
String tzid = "GMT" + source.substring(28, 31)
+ ":" + source.substring(31, 33);
TimeZone tz = TimeZone.getTimeZone(tzid);
// if (tz == null) ?
SimpleDateFormat f = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss");
f.setTimeZone(tz);
Date date = f.parse(source);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.out.println(date);
Prints "Wed Aug 17 08:57:09 UTC 2011".
A more sophisticated approach would be to use regex to extract individual parts ("+/-", "hh" and "mm") of the time zone offset.
Alternatively, you can attempt to discern the 3-letter time zone id (the string in between ( and )), and use the corresponding Java TimeZone if it exists.
In your particular example, though, "BST" resolves to Bangladesh Time which is GMT+0600 so you're better off with the numeric offset. "BST" here should probably be taken as British Summer Time (GMT+0100). This can be important because numeric offsets do not indicate the use of daylight savings time, which can be in effect depending on the date.
A more heuristic routine could take this into account and attempt to resolve the name first, but verify that the GMT offsets match, and fallback on the simple "GMT+hh:mm" timezones otherwise.
If you can not find a pattern matching your use case, try:
try{
new Date("Wed Aug 17 2011 09:57:09 GMT+0100 (BST)")
}catch(Exception e)
{
// Parse exception
}

Categories

Resources