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().
Related
I´m trying to pase the next String using LocalDateTime, but I always get de unparsed text found error:
java.time.format.DateTimeParseException: Text '2020-10-16T18:04:59+0300' could not be parsed at index 24
Need: from 2020-10-16T18:04:59+0300 to 2020-10-16 18:04.
My code:
public String getFormattingData(String sourceData) {
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ''e", Locale.ENGLISH);
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("dd-MM-yyy HH:mm", Locale.ENGLISH);
LocalDate date = LocalDate.parse("2020-10-16T18:04:59+0300", sourceFormatter);
return newFormatter.format(date);
}
What am I doing wrong?
See the related question: Format a date using the new date time API
The source format you are looking for is: "yyyy-MM-dd'T'HH:mm:ssZ" (as mentioned by #Sweeper in his comment)
If you want the HH:mm in the output format, you need to use a LocalDateTime rather than a LocalDate
The code below works for me:
public String getFormattingData(String sourceData) {
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ENGLISH);
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm", Locale.ENGLISH);
LocalDateTime date = LocalDateTime.parse("2020-10-16T18:04:59+0300", sourceFormatter);
return newFormatter.format(date);
result:
16-10-2020 18:04
You just need to correct the date pattern of source date like this:
public static String formatDate(String strDate, String srcPattern, String tgtPattern) {
DateTimeFormatter srcFormatter = DateTimeFormatter.ofPattern(srcPattern, Locale.ENGLISH);
DateTimeFormatter tgtFormatter = DateTimeFormatter.ofPattern(tgtPattern, Locale.ENGLISH);
return tgtFormatter.format(LocalDateTime.parse(strDate, srcFormatter));
}
You can also use SimpleDateFormat:
public static String formatDate(String strDate, String srcPattern,
String tgtPattern) throws ParseException {
SimpleDateFormat srcFormatter = new SimpleDateFormat(srcPattern, Locale.ENGLISH);
SimpleDateFormat tgtFormatter = new SimpleDateFormat(tgtPattern, Locale.ENGLISH);
return tgtFormatter.format(srcFormatter.parse(strDate));
}
And then call it with any pattern that you want:
System.out.println(formatDate("2020-10-16T18:04:59+0300", "yyyy-MM-dd'T'HH:mm:ssZ", "dd-MM-yyy HH:mm"));
Don’t write a method that converts a date and time from a string in one format to a string in a different format. In your program keep dates and times as proper date-time objects. Just like you don’t keep numbers and Boolean values in strings (I hope!) When you receive string input, parse into a date-time object at once. Only when you need to give string output, format into an appropriate string.
When I receive a string containing date, time and UTC offset, like yours does, I prefer to parse it into a OffsetDateTime so I get all the information. It’s easier to throw unneeded information away later than to invent the information that we neglected to parse. Also a LocalDate will not work for your purpose since it doesn’t contain time of day. So you cannot format one into 2020-10-16 18:04 format.
For parsing your string I would use:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendOffset("+HHmm", "Z")
.toFormatter();
String sourceData = "2020-10-16T18:04:59+0300";
OffsetDateTime dateTime = OffsetDateTime.parse(sourceData, formatter);
System.out.println(dateTime);
Output is:
2020-10-16T18:04:59+03:00
The definition of the formatter is longish but has the advantage of reusing predefined formatters for date and time.
For displaying a formatted date and time to the user, don’t you want to use the user’s time zone rather then the offset that happened to be in the string (+03:00 in your case)?
ZoneId zone = ZoneId.of("Antarctica/South_Pole");
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm", Locale.ENGLISH);
String formatted = dateTime.atZoneSameInstant(zone).format(newFormatter);
System.out.println(formatted);
17-10-2020 04:04
What went wrong in your code?
As others have said, yyyy-MM-dd'T'HH:mm:ssZ in your format pattern string for parsing parses your entire date-time string of 2020-10-16T18:04:59+0300 nicely. The ''e at the end of the format pattern is the culprit. This would require an additional single quote (apostrophe) and the number of the day of the week to be present (pattern letter e is for localized day of week). Since Java had successfully parsed 24 chars and then failed to parse an apostrophe, it threw the exception mentioning thst the string could not be parsed at index 24.
I have a DateTime in the format of Tue, 30 Apr 2019 16:00:00 +0800 which is RFC 2822 formatted date
I need to convert this to the given timezone in the DateTime which is +0800
So if i summarized,
DateGiven = Tue, 30 Apr 2019 16:00:00 +0800
DateWanted = 01-05-2019 00:00:00
How can i achieve this in Java?
I have tried the below code but it gives 08 hours lesser than the current time which is
30-04-2019 08:00:00
Code i tried
String pattern = "EEE, dd MMM yyyy HH:mm:ss Z";
SimpleDateFormat format = new SimpleDateFormat(pattern);
Date startDate = format.parse(programmeDetails.get("startdate").toString());
//Local time zone
SimpleDateFormat dateFormatLocal = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
//Time in GMT
Date dttt= dateFormatLocal.parse( dateFormatGmt.format(startDate) );
You are on right approach but just use java-8 date time API module, first create DateTimeFormatter with the input format representation
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z");
And then use OffsetDateTime to parse string with offset
OffsetDateTime dateTime = OffsetDateTime.parse("Tue, 30 Apr 2019 16:00:00 +0800",formatter);
And the call the toLocalDateTime() method to get the local time
LocalDateTime localDateTime = dateTime.toLocalDateTime(); //2019-04-30T16:00
If you want the output in particular format again you can use DateTimeFormatter
localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) //2019-04-30T16:00:00
Note : As #Ole V.V pointed in comment, after parsing the input string into util.Date you are getting the UTC time
The class Date represents a specific instant in time, with millisecond precision.
So now if you convert the parsed date time into UTC you get 2019-04-30T08:00Z without offset, so you can use withOffsetSameInstant to convert it into any particular timezone
dateTime.withOffsetSameInstant(ZoneOffset.UTC)
You misunderstood. According to RFC 2822 +0800 means that an offset of 8 hours 0 minutes has already been applied to the time compared to UTC. So the output you got was the correct GMT time.
java.time
I recommend you skip the old and outdated classes SimpleDateFOrmat and Date. It’s much nicer to work with java.time, the modern Java date and time API. Furthermore it has the RFC format built in, so we don’t need to write our own formatter.
OffsetDateTime parsedDateTime = OffsetDateTime
.parse("Tue, 30 Apr 2019 16:00:00 +0800",
DateTimeFormatter.RFC_1123_DATE_TIME);
ZonedDateTime dateTimeInSingapore
= parsedDateTime.atZoneSameInstant(ZoneId.of("Asia/Singapore"));
System.out.println("In Singapore: " + dateTimeInSingapore);
OffsetDateTime dateTimeInGmt
= parsedDateTime.withOffsetSameInstant(ZoneOffset.UTC);
System.out.println("In GMT: " + dateTimeInGmt);
Output:
In Singapore: 2019-04-30T16:00+08:00[Asia/Singapore]
In GMT: 2019-04-30T08:00Z
The built-in formatter is named RFC_1123_DATE_TIME because the same format is used in multiple Requests for Comments (RFCs).
Links
RFC 2822 Internet Message Format
Oracle tutorial: Date Time explaining how to use java.time.
with the help of #ole v.v's explanation i have separated the datetime value for two
1. time
2. timezone
then i used this coding to extract the datetime which is related to the given timezone
//convert datetime to give timezone
private static String DateTimeConverter (String timeVal, String timeZone)
{
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat offsetDateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
offsetDateFormat2.setTimeZone(TimeZone.getTimeZone(timeZone));
String result =null;
try {
result = offsetDateFormat2.format(format.parse(timeVal));
} catch (java.text.ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
I have two strings that I want to convert into a particular date time format so I can do a comparison. Problem I have is that it errors out in the parse with an exception and I wonder if I am doing something wrong. Wanted to ask what is the best way to convert two different string dates into a single date format
SimpleDateFormat localDateFormat = new SimpleDateFormat("yyyy dd mm - HH:mm:ss");
String firstDateString= "11 May 2018 21:03:51 GMT";
String secondDateString= "dataStore.get("2018-05-11T21:03:51Z";
Date firstDateFormat =localDateFormat.parse(firstDateString);
Date secondDateFormat =localDateFormat.parse(secondDateString);
Problem I have is that it errors out in the parse with an exception
and I wonder if I am doing something wrong.
=> Yes you are doing it actually. You first need to parse the date into it's actual format and then format it into the desired format.
For example: for parsing and formatting 2018-05-11T21:03:51Z
DateFormat originalFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:MM:SS'z'", Locale.ENGLISH);
DateFormat targetFormat = new SimpleDateFormat("yyyy dd mm - HH:mm:ss");
Date date = originalFormat.parse("2018-05-11T21:03:51Z");
String formattedDate = targetFormat.format(date); // 2018 05 11 - 21:03:51
Here:
SimpleDateFormat localDateFormat = new SimpleDateFormat("yyyy dd mm - HH:mm:ss");
That format says: 4 year digits SPACE 2 day digits SPACE 2 month digits DASH and so on.
Thing is: neither your string dates:
"11 May 2018 21:03:51 GMT"
"2018-05-11T21:03:51Z"
Look like that. The first one is rather "dd M yyy ..." (doesnt start with year), and the second one uses "-" not " " as separator for the initial date.
Answer: you have to use a pattern that really matches the expected date strings, see here for the specs. And note for example that you will need to use M to match "May", the lowercase m is about digits, not words!
And note: the second example is an ISO date, and the DateTimeFormatter already has pre-defined formatters for those! (so be careful about re-inventing the wheel)
java.time
DateTimeFormatter firstFormatteer
= DateTimeFormatter.ofPattern("d MMM uuuu H:mm:ss z", Locale.ENGLISH);
String firstDateString = "11 May 2018 21:03:51 GMT";
String secondDateString = "2018-05-11T21:03:51Z";
Instant firstInstant = firstFormatteer.parse(firstDateString, Instant::from);
Instant seoncdInstant = Instant.parse(secondDateString);
System.out.println("The strings are parsed into " + firstInstant + " and " + seoncdInstant);
Output is:
The strings are parsed into 2018-05-11T21:03:51Z and 2018-05-11T21:03:51Z
Your strings from two services are in two different formats, and the best you can do is to handle them in two different ways. For the first, define a formatter that matches the format. The second is in ISO 8601 format. Instant parses this format without any explicit formatter, so here we don’t need to define one.
To compare do for example:
if (firstInstant.isBefore(seoncdInstant)) {
System.out.println("The first date and time comes first");
} else if (firstInstant.equals(seoncdInstant)) {
System.out.println("The date and time is the same");
}
The date and time is the same
The Instant class is the modern replacement for the Date class, it represents a moment in time.
The Date class was poorly designed and SimpleDateFormat notoriously troublesome, fortunately they are both long outdated. I recommend you avoid them and use java.time, the modern Java date and time API, instead.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I am retrieving a date/time from an external data source, this is returned in the following format "14:30 Sat 05 May" with no year.
I've been trying to parse this to a LocalDateTime unsuccessfully. The data returned does not return a year as it is an assumption that we are always operating in the current year.
//date to parse
String time = "14:30 Sat 05 May";
//specify date format matching above string
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm E d MMM YYYY") ;
//we do not have a year returned but i can make the assumption we use the current year
LocalDateTime formatDateTime = LocalDateTime.parse(time, formatter).withYear(2018);
The above code throws the following exception
Exception in thread "main" java.time.format.DateTimeParseException: Text '14:30 Sat 05 May' could not be parsed at index 16
Any help appreciated.
Default year
Specify a default year in your DateTimeFormatter, using the DateTimeFormatterBuilder class by calling parseDefaulting and specifying the year-field with ChronoField.YEAR.
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("HH:mm E d MMM")
.parseDefaulting(ChronoField.YEAR, 2018) // <------
.toFormatter(Locale.ENGLISH);
With this formatter instead of yours:
LocalDateTime.parse( "14:30 Sat 05 May" , formatter )
…I get:
2018-05-05T14:30
See that code run live at IdeOne.com.
Points to note:
Your format pattern string needs to match the parsed string end-to-end. So when your date-time string doesn’t have a year in it, don’t include YYYY in your format pattern.
In any case don’t use uppercase YYYY here. It’s for week-based year and only useful with week numbers. If your string had had a year in it, you should have used uuuu or lowercase yyyy.
Make it a habit to give explicit locale to your formatter so you know it also works on other computers, and on yours when one day you play with its settings.
LocalDateTime.parse() expects a String that represents a valid date, which the year part.
You cannot set the year after invoking this method in this way :
LocalDateTime.parse(time, formatter).withYear(2018);
The year has to be set before because otherwise parse() throws DateTimeParseException.
As a workaround you may concatenate the current year in the input.
Some additional notes:
the pattern you use and the input date in textual format don't match exactly.
You don't specify a Locale for the parsing operation.
It means that it will work according to the local where the JVM is run.
To ensure that it works in any case, you should specify the Locale.
So you could try something like :
//date to parse
String time = "14:30 Sat 05 May";
time += " " + LocalDate.now().getYear();
//specify date format matching above string
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm EEE dd MMM yyyy", Locale.US) ;
//we do not have a year returned but i can make the assumption we use the current year
LocalDateTime formatDateTime = LocalDateTime.parse(time, formatter);
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));